Quellcode durchsuchen

Merge branch 'develop' into feat/exporting

Hallvord R. M. Steen vor 4 Jahren
Ursprung
Commit
d5daa33dec
100 geänderte Dateien mit 15730 neuen und 3838 gelöschten Zeilen
  1. 2 1
      .appveyor.yml
  2. 1 0
      .eslintignore
  3. 3 0
      .eslintrc.yml
  4. 7 0
      .gitignore
  5. 2 3
      .travis.yml
  6. 346 2
      CHANGELOG.md
  7. 7 17
      LICENSE
  8. 12 18
      README.md
  9. 3033 0
      demo/BrahWiMeSample.musicxml
  10. 5929 0
      demo/BrookeWestSample.musicxml
  11. 117 0
      demo/embedded_demo.html
  12. 80 20
      demo/index.html
  13. 647 108
      demo/index.js
  14. 0 429
      external/vexflow/vexflow.d.ts
  15. 7 2
      karma.conf.js
  16. 55 39
      package.json
  17. 29 6
      src/Common/DataObjects/Fraction.ts
  18. 1 1
      src/Common/DataObjects/MusicSheetErrors.ts
  19. 23 4
      src/Common/DataObjects/Pitch.ts
  20. 10 0
      src/Common/Enums/TieTypes.ts
  21. 1 0
      src/Common/Enums/index.ts
  22. 14 11
      src/Common/FileIO/Mxl.ts
  23. 2 2
      src/Common/FileIO/Xml.ts
  24. 3 5
      src/Common/Strings/StringUtil.ts
  25. 1 2
      src/Common/index.ts
  26. 1 1
      src/MusicalScore/Exceptions.ts
  27. 6 2
      src/MusicalScore/Graphical/AbstractGraphicalExpression.ts
  28. 23 6
      src/MusicalScore/Graphical/AccidentalCalculator.ts
  29. 23 8
      src/MusicalScore/Graphical/BoundingBox.ts
  30. 26 2
      src/MusicalScore/Graphical/DrawingEnums.ts
  31. 76 13
      src/MusicalScore/Graphical/DrawingParameters.ts
  32. 590 1396
      src/MusicalScore/Graphical/EngravingRules.ts
  33. 11 5
      src/MusicalScore/Graphical/GraphicalChordSymbolContainer.ts
  34. 29 7
      src/MusicalScore/Graphical/GraphicalContinuousDynamicExpression.ts
  35. 1 1
      src/MusicalScore/Graphical/GraphicalCurve.ts
  36. 2 2
      src/MusicalScore/Graphical/GraphicalInstantaneousDynamicExpression.ts
  37. 1 1
      src/MusicalScore/Graphical/GraphicalInstantaneousTempoExpression.ts
  38. 65 25
      src/MusicalScore/Graphical/GraphicalLabel.ts
  39. 6 5
      src/MusicalScore/Graphical/GraphicalLyricEntry.ts
  40. 1 1
      src/MusicalScore/Graphical/GraphicalLyricWord.ts
  41. 23 10
      src/MusicalScore/Graphical/GraphicalMeasure.ts
  42. 32 20
      src/MusicalScore/Graphical/GraphicalMusicPage.ts
  43. 79 57
      src/MusicalScore/Graphical/GraphicalMusicSheet.ts
  44. 8 3
      src/MusicalScore/Graphical/GraphicalNote.ts
  45. 230 149
      src/MusicalScore/Graphical/GraphicalSlur.ts
  46. 11 10
      src/MusicalScore/Graphical/GraphicalStaffEntry.ts
  47. 2 2
      src/MusicalScore/Graphical/GraphicalStaffEntryLink.ts
  48. 3 0
      src/MusicalScore/Graphical/GraphicalTie.ts
  49. 39 0
      src/MusicalScore/Graphical/GraphicalUnknownExpression.ts
  50. 5 60
      src/MusicalScore/Graphical/GraphicalVoiceEntry.ts
  51. 373 230
      src/MusicalScore/Graphical/MusicSheetCalculator.ts
  52. 45 31
      src/MusicalScore/Graphical/MusicSheetDrawer.ts
  53. 89 51
      src/MusicalScore/Graphical/MusicSystem.ts
  54. 493 162
      src/MusicalScore/Graphical/MusicSystemBuilder.ts
  55. 96 38
      src/MusicalScore/Graphical/SkyBottomLineCalculator.ts
  56. 59 7
      src/MusicalScore/Graphical/StaffLine.ts
  57. 1 1
      src/MusicalScore/Graphical/SystemLine.ts
  58. 43 7
      src/MusicalScore/Graphical/SystemLinesEnum.ts
  59. 1 1
      src/MusicalScore/Graphical/VerticalGraphicalStaffEntryContainer.ts
  60. 44 12
      src/MusicalScore/Graphical/VexFlow/AlignmentManager.ts
  61. 80 31
      src/MusicalScore/Graphical/VexFlow/CanvasVexFlowBackend.ts
  62. 68 8
      src/MusicalScore/Graphical/VexFlow/SvgVexFlowBackend.ts
  63. 61 9
      src/MusicalScore/Graphical/VexFlow/VexFlowBackend.ts
  64. 7 3
      src/MusicalScore/Graphical/VexFlow/VexFlowContinuousDynamicExpression.ts
  65. 305 78
      src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts
  66. 43 17
      src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalNote.ts
  67. 57 20
      src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalSymbolFactory.ts
  68. 3 1
      src/MusicalScore/Graphical/VexFlow/VexFlowInstantaneousDynamicExpression.ts
  69. 1 1
      src/MusicalScore/Graphical/VexFlow/VexFlowInstrumentBrace.ts
  70. 2 2
      src/MusicalScore/Graphical/VexFlow/VexFlowInstrumentBracket.ts
  71. 437 108
      src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts
  72. 155 0
      src/MusicalScore/Graphical/VexFlow/VexFlowMultiRestMeasure.ts
  73. 430 182
      src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts
  74. 137 50
      src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts
  75. 15 10
      src/MusicalScore/Graphical/VexFlow/VexFlowMusicSystem.ts
  76. 31 16
      src/MusicalScore/Graphical/VexFlow/VexFlowOctaveShift.ts
  77. 2 2
      src/MusicalScore/Graphical/VexFlow/VexFlowSlur.ts
  78. 13 6
      src/MusicalScore/Graphical/VexFlow/VexFlowStaffEntry.ts
  79. 7 0
      src/MusicalScore/Graphical/VexFlow/VexFlowStaffLine.ts
  80. 119 0
      src/MusicalScore/Graphical/VexFlow/VexFlowTabMeasure.ts
  81. 8 3
      src/MusicalScore/Graphical/VexFlow/VexFlowTextMeasurer.ts
  82. 141 1
      src/MusicalScore/Graphical/VexFlow/VexFlowVoiceEntry.ts
  83. 176 0
      src/MusicalScore/Graphical/VexFlow/VexflowStafflineNoteCalculator.ts
  84. 2 1
      src/MusicalScore/Graphical/index.ts
  85. 12 0
      src/MusicalScore/Instrument.ts
  86. 9 3
      src/MusicalScore/Interfaces/IGraphicalSymbolFactory.ts
  87. 7 0
      src/MusicalScore/Interfaces/IStafflineNoteCalculator.ts
  88. 2 1
      src/MusicalScore/Interfaces/ITextMeasurer.ts
  89. 1 1
      src/MusicalScore/Interfaces/ITextTranslation.ts
  90. 0 1
      src/MusicalScore/Interfaces/ITransposeCalculator.ts
  91. 7 2
      src/MusicalScore/Label.ts
  92. 8 8
      src/MusicalScore/MusicParts/MusicPartManager.ts
  93. 54 42
      src/MusicalScore/MusicParts/MusicPartManagerIterator.ts
  94. 77 46
      src/MusicalScore/MusicSheet.ts
  95. 1 1
      src/MusicalScore/MusicSource/MappingSourceMusicPart.ts
  96. 5 5
      src/MusicalScore/MusicSource/Repetition.ts
  97. 214 85
      src/MusicalScore/ScoreIO/InstrumentReader.ts
  98. 78 62
      src/MusicalScore/ScoreIO/MusicSheetReader.ts
  99. 62 24
      src/MusicalScore/ScoreIO/MusicSymbolModules/ArticulationReader.ts
  100. 24 12
      src/MusicalScore/ScoreIO/MusicSymbolModules/ChordSymbolReader.ts

+ 2 - 1
.appveyor.yml

@@ -3,7 +3,8 @@ environment:
   timeout: 10000
   matrix:
     # - nodejs_version: "8" 
-    - nodejs_version: "10"
+    # - nodejs_version: "10"
+    - nodejs_version: "12"
 platform:
   # - x86
   - x64

+ 1 - 0
.eslintignore

@@ -3,3 +3,4 @@ dist
 build
 bin
 demo
+src/VexFlowPatch

+ 3 - 0
.eslintrc.yml

@@ -1,3 +1,6 @@
+parserOptions: {
+        ecmaVersion: 8, // 8 = ECMA2017 necessary for promises/async. The ecmaVersion isn't set in stone, for now mostly adapted for eslint.
+    }
 extends: standard
 rules:
   indent: [2, 4]

+ 7 - 0
.gitignore

@@ -14,6 +14,10 @@ pids
 *.pid
 *.seed
 
+# optional npm script-generated data
+visual_regression/
+export/
+
 # Documentation
 docs
 
@@ -58,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

+ 2 - 3
.travis.yml

@@ -2,9 +2,8 @@ sudo: required
 dist: trusty
 language: node_js
 node_js:
-# - '6'
-# - '8'
-- '10'
+# - '10' # fails on Travis since upgrading to Vexflow 1.2.90 (still passes on AppVeyor), for Mxl_Test. Node 12 works.
+- '12'
 env:
   - timeout=10000
 notifications:

+ 346 - 2
CHANGELOG.md

@@ -1,11 +1,355 @@
-## [0.6.8](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.6.6...0.6.8) (2019-02-08)
+## [0.8.7](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.8.6-demofix...0.8.7) (2020-11-05)
 
 
 ### Bug Fixes
 
-* **alignment:** fix alignment of notes following dotted rest ([a7eb53d](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/a7eb53d)), closes [#484](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/484)
+* **Accidentals:** Remember quarter sharp/flat, don't automatically put a natural after them ([#903](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/903)) ([0696624](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/0696624d7765144440ad47c32599196aa6debac5))
+* **Beams:** Fix beams in tuplets with disconnected stems ([#907](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/907)) ([8a97d47](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/8a97d4754bc990eddc8aeb9c75e743b452088d26))
+* **Beams:** Fix beams with tuplets ([#907](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/907)). Add EngravingRules FlatBeams, FlatBeamOffset, FlatBeamOffsetPerBeam ([7207676](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/7207676c2882d97fa8252221d8de8018dfadfc1e))
+* **Beams:** Fix nested beams, erroneous xml beam numbers ([#909](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/909)). Save IsGrace in Note ([a0df576](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/a0df576aa6673fc89c900ed559ad07926d2c6ae1))
+* **ChordSymbols:** Render Natural Harmonic ([#887](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/887))
+* **Compact mode:** Compact mode is now even more compact, doesn't add system margin ([#898](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/898))
+* **Empty Measures:** Prevent a Vexflow bug where a measure was empty because a modifier width was NaN ([a0dbc4f](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/a0dbc4f6254b723d15007457bd82dfe36810eeb5)), closes [#899](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/899) [#49](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/49)
+* **Fingering:** Associate fingering with correct note when not all notes have fingerings, save Note.Fingering ([#889](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/889)) ([a59e5d9](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/a59e5d97398b6b22ef6b410aa8586fc8847885f3))
+* **Iterator:** Fix iterator.clone(): start at iterator.currentTimeStamp if startTimeStamp undefined ([#896](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/896))
+* **Layout:** Don't add first system's border to margin below title. Saves a lot of space. ([5c32ff1](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/5c32ff14be9efb0653ceda267613c500234b409a)), closes [#898](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/898)
+* **Octave Shifts:** Fix ottavas (octave shifts) not generated correctly over multiple systems ([#591](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/591)) (PR [#777](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/777)) ([11e9c20](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/11e9c20beccaa01e075f4994515c95e9ec9cf896))
+* **Rhythm:** Don't print rhythm twice even if given in pickup measure and following measure ([#890](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/890)) ([d34f5e4](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/d34f5e4d41d15b154e30d9fb3cae630f3430a3b2))
+* **Skyline:** Replace undefined values with neighboring values. Fix some tab scores ([#911](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/911)) ([e824928](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/e824928983f84e6f72d28394f037be7621cbc56d))
+* **Zoom:** Fix pageBackgroundColor not filling entire page for zoom < 1 ([#904](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/904)) ([d795e7b](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/d795e7b90f8530bbe615ef696dc217b76f4e5e7c))
+* **Zoom:** Fix using += with osmd.Zoom by adding osmd.Zoom getter ([3cb7fc2](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/3cb7fc22d3ce695c3605ce74352ef47da52b4d55)), closes [mpat#75](https://github.com/mpat/issues/75)
+
+
+### Features
+
+* **Slurs:** Take slur placement from XML by default ([#827](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/827)). add EngravingRules.SlurPlacementFromXML ([4cb3de9](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/4cb3de9183697357c1039794b66ad016e6253d9f))
+* **Log:** Add log level silent (no console.logs)
+* **osmd.Sheet:** Add setter for TitleString, SubtitleString, ComposerString, LyricistString (no need to give Label)
+* **Tremolo:** Add TremoloStrokeScale and TremoloYSpacingScale in EngravingRules ([887](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/887))
+
+
+
+## [0.8.6](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.8.5...0.8.6)  (2020-09-15)
+
+### Bug Fixes
+* **Voltas, Tabs**: Fix regression in 0.8.5 where repetition volta shift and tabnote svg id fixes were not applied (will soon be automatically fixed by vexflow patch script)
+
+### Miscellaneous
+* **Build**: Build size down to 1.1MB again from mysterious increase to 1.3MB in 0.8.5
+
+## [0.8.5](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.8.4...0.8.5) (2020-09-08)
+
+
+### Bug Fixes
+
+* **Container height:** Small scores don't significantly exceed bounding box anymore (SVG height). Fix PageBottomMargin ([#875](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/875)) etc ([c43565c](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/c43565c970e2344f7867d463e6885b8eaa63f204)), closes [#788](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/788)
+* **Rests:** Fix rests in pickup measures (e.g. 8th pickup in 4/4 time) turned into whole measure rests or multiple measure rests. ([f1478a6](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/f1478a6e52c34b4ea3710be1cec197c3e445981f))
+* **Tabs:** Fix multi-rest for tabs. Never create fingerings for tabs, for now ([ed8d174](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/ed8d174ee2137f8bdbc665ed9c70ef0d050c631a))
+
+
+### Features
+
+* **Measure Numbers:** Display measure numbers (labels) as given in XML ([#541](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/541)) ([6f5d77a](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/6f5d77a9efbe29d8e210c8715c01548f1bc067f3)), closes [#879](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/879)
+* **Options:** Add drawUpToPageNumber and drawUpToSystemNumber options ([#835](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/832))
+
+
+
+## [0.8.4](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.8.3...0.8.4) (2020-08-14)
+
+
+### Bug Fixes
+
+* **Barline:** Better detect measure ending barline from XML in some cases ([#868](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/868)) ([bfc5892](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/bfc5892f005b5d236036841de5a2773288fc14d0))
+* **Beams:** Fix beams with bad slopes by always recalculating beams. ([#843](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/843)) ([7b1297e](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/7b1297ea2c2e59b69e2007f88d5d587ea9e264b7))
+* **Chords:** Prevent undefined error ([2cff998](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/2cff9981d8ea2627f69f927c9fa27838579e80ab))
+* **Cursor:** Attach to correct page HTMLElement (canvas) ([#817](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/817)) ([decb3f1](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/decb3f11fd2979e5e41e50d6e2aa4f789197e8aa))
+* **Cursor:** Position correctly on page 2+ ([#817](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/817)) ([ca7dffc](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/ca7dffce3c5c895ae51e4b44f23e0c6b7dc1b555))
+* **Cursor:** Show correctly for MultiRestMeasures, interpolated by MeasureNumber (progress) ([f80697b](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/f80697b1c8c18eec60c3a180edbed421a8e5f0ab))
+* **Layout:** Don't automatically create new system after final barline. add EngravingRules.NewPartAndSystemAfterFinalBarline ([7a0a1e4](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/7a0a1e4867d210611495919934ac599e4373cee0))
+* **Layout:** Fix lyrics dashes causing errors with drawUpToMeasureNumber ([#838](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/838)), fix dynamic expressions missing ([1128c8b](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/1128c8b748913506d1958d53a101487b7a0f8948))
+* **MeasureNumbers:** Don't reset measure number count if this.rules.NewPartAndSystemAfterFinalBarline disabled ([0991aac](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/0991aac86ac0bb5e3a485fd926d4d21e646f9646))
+* **Metronome marks:** Draw all metronome marks, not just in first measure ([#804](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/804)) ([f37cabb](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/f37cabba987cc7ba57f50e66d3f1fe6e0c9f136a)), closes [#848](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/848)
+* **OctaveShift:** Fix occasional error when no start or end note found ([#860](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/860)) ([f91e5d0](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/f91e5d0b9a7812612a8397014e27d7002cc87a3f))
+* **OctaveShift:** Fix rare null error ([#860](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/860)) ([deb21e3](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/deb21e38017c7e06556e9656f0bf88bf99a35210))
+* **Ornaments:** Mordent and inverted mordent reversed (mordent with line), according to MusicXML definition ([#866](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/866)) ([1237bec](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/1237bec904bb76947598b35366869a02aa831041))
+* **Rest note positioning:** Rest notes respect the display-step and display-octave tags now ([#759](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/759)) ([e894bc8](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/e894bc80164d6ddc4e8b0464f6a1e8aa8c3fabe3)), closes [#852](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/852)
+* **Tabs:** By default, don't draw tuplet numbers in tabs ([#805](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/805)) ([89a9f0f](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/89a9f0f5ab970dc88c02438c078b786f5b83aee4))
+* **Tabs:** Fix setLedgerLineStyle not found on rests ([907fede](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/907fede813176057db5022b761ddd14a3e5dc914))
+* **Ties:** Ties render correctly again, fix tie cross-up ([#844](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/844)) ([efee071](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/efee0714a1a61b44b7b59dd71f0a80c893978a32))
+* **Tuplet:** Respect placement below from XML ([#867](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/867)) ([cf7ecfe](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/cf7ecfe0c21e93f946c57edb6abc8eeef3936b6b))
+* **Visible:** Fix some errors when setting sheet.Instrument[i].Visible to false on multiple instruments. ([9037f67](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/9037f67beb3360c715c82437460965b99593cc9e))
+* **Volta:** Fix y-alignment for multiple rest measures (fix [#789](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/789)) ([539e7d0](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/539e7d0015df6e17bd3e0102682d5681eb871a41))
+
+
+### Features
+
+* **Fonts:** Read and apply fontStyle for UnknownExpressions like dolce ([#348](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/348)) ([28b313d](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/28b313d73a2ea53869982d1b83bd8eb6d567ab75))
+* **Labels:** Add option for additional spacing between multiline text labels (EngravingRules.SpacingBetweenTextLines) ([b530316](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/b5303169be0fea7f160cec62ae12abbe413a62c3))
+* **Multirest:** Auto-generate multirest measure from subsequent rest-only measures ([#861](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/861)) ([f7d6424](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/f7d642489bfe365d59ef27057f44aa31867db84b))
+* **Options:** Add option StretchLastSystemLine to stretch last system to full width ([#842](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/842)) ([88d1a5a](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/88d1a5a225765ea2447494775355043221c623cc)), closes [#847](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/847)
+* **Options:** Add osmd.EngravingRules.LyricsYOffsetToStaffHeight ([#865](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/865)) ([3ca5dd0](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/3ca5dd08a257e2aba70222c72cb6c2ca84da313b))
+* **Rests:** For rests encircled by beamed notes, if no display-step given, put the rest on the same y-height as previous note to avoid collisions. ([a74d77b](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/a74d77b964c862dd92e28f0870018c1d675d6c1a)), closes [#66](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/66)
+
+
+
+## [0.8.3](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.8.2...0.8.3) (2020-07-28)
+
+
+### Bug Fixes
+
+* **MetronomeMark:** render correct beat unit (e.g. half instead of quarter). fix [#828](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/828) ([b2fb539](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/b2fb539f800e83c1adbb9bcdee76acc3d1e78f2c))
+* **Parsing:** fix missing default-y value for credit-words causing parse error ([4773487](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/47734879482a89068286cbd79f63b00db46ebff4)), closes [#62](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/62)
+* **Repeat:** don't draw vertical line at the beginning of a system for single staff systems ([#834](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/834)) ([e556978](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/e55697812581e1f1b60f391cdaadfdca38fca2d5))
+* **Slur:** Slurs avoid note collision ([c114296](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/c114296369e2705ac7e528d00129c58d4d7a5b39))
+* **Slur:** Slurs avoid note collision ([#802](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/802)) ([18397f2](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/18397f2be60f1ea4f134d807c437bcbdc5eb5090))
+* **Slur:** Tweaks to slur rendering from matt-uib ([b6d628f](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/b6d628f0b02296979632e1ac3a5fe5491f4c865a))
+* **Tabs:** by default, don't draw tuplet numbers in tabs ([#805](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/805)) ([89a9f0f](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/89a9f0f5ab970dc88c02438c078b786f5b83aee4))
+* **Transparency:** ledger lines of invisible notes are now invisible as well. ([#799](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/799)) ([d73e8ac](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/d73e8acddfeddb1d68f8e927d541d8ed3819ce40))
+* fixed right alignment (and also x-center alingment) of text lines in multi-line-label (e.g. for Composer on the right side) ([3988fde](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/3988fdec09bf698f62cb7c39a0b07d324f05a705))
+
+
+### Features
+
+* **Articulation:** render marcato, up and down ([8104dfa](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/8104dfa415a1b8abc915bac2cfd761402819fb79))
+* **Label:** Add support for no-print on part names ([c3e6c8d](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/c3e6c8d878ab2ed64afbcc562ef6cc2cb7af61a1))
+* **Label:** Add support for multiline labels ([#801](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/801)) ([eda4cbf](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/eda4cbf40b17f8781f45bb1abf40730dc80a2640))
+* **Lyrics:** Spacing: add maximum measure elongation factor in EngravingRules. ([b575746](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/b575746af1318916984b5a2be307ca99aaf1ac02))
+* **MultipleRest:** Render multiple measure rests. add option to not render them. ([#506](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/506)) ([7a04814](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/7a04814e7f77a3c59006116a95ba7d3010f489f6))
+* **Noteheads:** support breve type (brevis) ([#803](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/803)) ([09fc5e7](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/09fc5e74e46edb4dbc37349638fe1003e99190ec))
+* **Options:** add EngravingRules.PageBottomExtraWhiteSpace ([#788](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/788)). merge [#822](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/822) ([6e5ae88](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/6e5ae88b4b84dbd6c0296deccbd5969f6c4ebc9e))
+* **Options:** add new EngravingRules for notationFontScale, stemWidth, staffLineColor ([#836](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/836)) ([2e1ab25](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/2e1ab25fe02cc88af06e334098eae5aef090c3df))
+* **Options:** add option to draw measure numbers only at the start of a system (line) ([ca9b88e](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/ca9b88ee7d135f0ff830e65320cce0e4a993d5d6)), closes [#59](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/59)
+* **Options:** add option to not render time signatures (drawTimeSignatures: false). ([#793](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/793)) ([21b089b](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/21b089b4841c648ec22f7c34c685dbaa945972d6))
+* **Options:** set default ledger line color to black (instead of grey). set in EngravingRules.LedgerLineColorDefault ([cb31c98](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/cb31c9817b91c72ed1758659b02939168d269ffe)), closes [#799](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/799)
+* **Spacing:** Tweaks to compress spacing more ([#820](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/820)) ([682f77c](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/682f77c613d764882a0d449d864a30417b6a7368))
+* **Spacing:** optimize vexflow voice spacing/scaling, refactor ([817762a](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/817762a593aeb8bd13b49e550203b28ece698843))
+* **Tabs:** Add Slides, Bends, Hammer-on and Pull-offs (PR [#839](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/839)) ([8de1400](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/8de1400ce0b51c55050d9961d1d2db16a0cb2d37))
+
+
+
+## [0.8.2](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.8.1...0.8.2) (2020-06-13)
+
+
+### Features
+
+* **PDF:** Remove createPDF from OSMD object (move to demo), reducing bundle size (from 1.5MB to 1.1MB). Fix FileReader pollution ([64c8ccf](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/64c8ccff943bbbedbfb85306f516d16ad7dfa0b8))
+
+
+## [0.8.1](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.8.0...0.8.1) (2020-06-10)
+
+### Bug Fixes
+
+* **OctaveShift:** avoid some errors when end of octaveshift not found ([#778](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/778)) ([86e6726](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/86e6726f4b3cf7f1ebde4deb7e33de5c57237167)), closes [#777](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/777) [#591](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/591)
+
+# [0.8.0](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.7.6...0.8.0) (2020-06-10)
+
+
+### Bug Fixes
+
+* **Beams:** Recalculate beams on resize, so they fit to stem ([#724](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/724)) ([50b0864](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/50b08643c1c980f709bda1cb8134a0f995a40c0f))
+* **ChordSymbols:** add 5 for power chord (e.g. C5, C plus fifth) ([#760](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/760)) ([6e1558d](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/6e1558d031452338a5cf0a0479f971337c6abbaf))
+* **ChordSymbols:** read augmented and dim in abbreviated form (aug, dim) ([a291f6f](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/a291f6f7c1fa023aaf0926fdfd05d4b97cd8fabe))
+* **ChordSymbols:** write Cm(maj7) for C kind 'major-minor' ([#784](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/784)) ([d00f29e](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/d00f29ed32b2496ce06d6955a481de2d8bcfc626))
+* **Cursor:** fix cursor undefined for canvas backend, improve cursor creation ([#736](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/736)) ([cb193d2](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/cb193d2270d4aec4e7847615b1c811e7ef4ba25a))
+* **Layout:** more lenient Measure Number Label collision checks, place them slightly to left ([#782](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/782)) ([616de17](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/616de1738b9e6466b0474bb1caa5985b11ae3bb1))
+* **Rendering:** catch Vexflow errors while rendering a measure, prevent render loop stopping. ([#773](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/773)) ([e2079a3](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/e2079a3f9de1183c3c9bb4593f5badbc4da50783))
+* Refactor undefined checks to also check null (#783) ([12766fb](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/12766fbf635ea3ec3e7c7b71fc05640354d726e2))
+* **Options:** drawPartNames: false now also disables drawPartAbbreviations, unless set explicitly ([#388](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/388)) ([cd50b68](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/cd50b68c4430ccbc05300aa1a231ec65dbe987c0))
+* **Skyline:** Fix Measure number skyline offset ([b97439e](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/b97439e3e5d8c3845e4e55c3b30fc0c92c726a11))
+* fixed breaking the system if the last measure has an endline (thin-bold line) ([9d98357](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/9d9835703ae2c7d4e6f18d6781294ff22a6b8c5b))
+* implemented always showing the current rhythm if there was an end line given in the last system. ([b2b31bf](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/b2b31bff84291672a3e8395d15799dc501a2a14d))
+* **Expressions:** don't align expressions when there are no edges involved. manual merge of [#768](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/768) ([ff77b46](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/ff77b467c76695fb419957c191bfdbf753d68cce))
+* **Expressions:** fix alignment in most cases, fix distance calculation ([#758](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/758), [#768](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/768)) ([6d5e752](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/6d5e75222d67f867d13c1273483c8019f6d32182))
+* improved chord symbol text output and fixed transposing code. ([de2ef57](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/de2ef57c278a220e1b0a985520b41fc1fe354bdd))
+* improved slur fix - removed magic number. Should work now for tabs as well. ([f67428c](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/f67428ca4103d1dce9d5d41126fe4a08c541e2bf))
+* removed doublettes check for dynamic expressions, as they might make sense for e.g. a repetition start, where another dynamic was used at the end of the repetition. ([9ea9814](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/9ea9814d6e61d0e1500959f02745814e4d27d137))
+* **Layout:** update Vexflow to 1.2.91 (now 1.2.93), fixing time signature yPos. adapt vexflow import. [#706](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/706) ([7de0f7d](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/7de0f7deaff462abf45a17e86db2d5bbb85ba697))
+* **Subtitle:** don't add comma before work number if subtitle is otherwise empty ([36e4e2a](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/36e4e2add7f3a7c4dbb0977ba0f3376623d4a6e4))
+
+
+### Features
+
+* **Cursor:** restore cursor state after resize and re-render ([#734](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/734)) if option set (default true) ([a08e957](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/a08e9578c24d6ef66917617dcf71e3dc2d5b72e7))
+* **Cursor:** unique HTMLElement id (cursor.cursorElementId) ([d8a15b2](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/d8a15b26762248711443c0115306f085dc29d091))
+* **Font:** can set fontFamily per label ([#737](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/737)), refactor: don't set defaultFontStyle per label ([a7af16b](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/a7af16bb49c09862a68f92350597d088d41a2204))
+* **Iterator:** clone() optionally starts from a given startTimeStamp (not fully tested) ([5d52d18](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/5d52d186ad9b165a08b8adcc51b26247aa3e6f94))
+* **Options:** able to set ledger line width and style (color) in EngravingRules. ([f4c2fc3](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/f4c2fc306d329a64bb14d0fbfd1d753fd3536dac))
+* **Options:** add option to not draw metronome marks ([#680](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/680)) ([42a1ebe](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/42a1ebe32034239aab82f377e308d2e373cc5b7b))
+* **Options:** can modify chord symbol text for all chords (except major). ([#784](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/784)) ([6f53f1c](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/6f53f1c1bf05d9af23f0392eb41d28249f66bde9))
+* **Text:** can set the fontStyle of a label, e.g. Bold (1) or BoldItalic (3). new option defaultFontStyle ([#739](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/739)) ([894868b](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/894868b051fa7d0eefd07a29d49d2f7065b357e9))
+
+
+
+## [0.7.6](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.7.5...0.7.6) (2020-04-17)
+
+### Features
+
+* **Layout:** support XML page breaks optionally. new option newPageFromXML. [#702](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/702) ([2bcec40](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/2bcec40e70c6209675a1d521630ef268eeb6d3a1))
+* **Options:** start new system ("line break") when given in XML and OSMDOptions.newSystemFromXML set ([#702](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/702)) ([5284aaa](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/5284aaa5e0a400731182d5ff36afc6e725465f55))
+* **Options:** add drawingparameter "compacttight" for reduced margins and tighter spacing ([085ff1a](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/085ff1a91beb080376f769070382554e3bc298af))
+
+
+## [0.7.5](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.7.4...0.7.5) (2020-04-15)
+
+
+### Bug Fixes
+
+* **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))
+* **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))
+
+### Features
+* **Options:** Instances of osmd don't share static options/EngravingRules anymore. Can have multiple independently on one page ([#559](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/559)) ([fc095ad](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/fc095ad4b5c2fbc68a220ccd518c70fbbf26a477))
+* **Tabs/Testing:** Add function test and testing/demo sample for guitar tabs ([e18f133](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/e18f1331facae39cd47e3ffb383448af8752a764))
+
+## [0.7.4](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.7.3...0.7.4) (2020-04-14)
+
+### Bug Fixes
+
+* **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))
+* **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))
+* **Demo/Embedding:** hide debug controls before rendering, always check showHeader option, only show debug controls by default on dev server ([#661](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/661)) ([8b60397](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/8b6039794ccf0617c1446f633cd2367a55e3b15b))
+* **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)
+* **Lyrics:** fix null reference for lyrics dashes when drawing range set ([a19b3cd](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/a19b3cd6b8f74859d534a685343c45776bcea42f))
+* **Repetition:** don't render a downward jog at type 'discontinue' ([#656](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/656)) ([996847d](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/996847db098859b6934dc74b3610eb1a3e40b594))
+* **Ties:** prevent undefined tieNotes from creating a Vexflow error and crashing rendering ([6209cd3](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/6209cd32257fb0639e3801e2a87e9f47cf0f9efa))
+* **Vexflow:** update vexflow to 1.2.90, fixing rests displayed twice (see OSMD function test invisible notes) ([a713d20](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/a713d20f44f3bb8314e2b9d417dd6cf4808f6817))
+
+
+### Features
+
+* **Tabs:** OSMD can now render guitar tabulature from MusicXML, see [PR #716](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/pull/716) and [issue #126](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/126#issuecomment-613615380)
+* **Color:** new option pageBackgroundColor. can set canvas color e.g. to white instead of transparent. ([#670](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/670)) ([4e5043c](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/4e5043c3440db6eb25fb9c85ab28ed62b703c3a9))
+* **Demo:** add showPageFormatControl, showExportPdfControl parameter, fix showZoomControl, hide header/controls before loading ([#661](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/661)) ([126d88e](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/126d88e1f0b4c6d86b24cce58912cf7deb56352e))
+* **Embedding:** add parameters for compact mode, measure range, page format. Revise PageFormat argument handling. ([#661](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/661)) ([5dc780e](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/5dc780edf0356a91a77a686a8509b9f9e81e753b))
+* **Embedding:** create backend option ([#661](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/661)), also, give warning when PDF is created on Canvas background. ([49dd902](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/49dd902f4196c9f978a1b1c8eb08c634e7c692ec))
+* **ExportPNG:** generate pngs for multiple pages when PageFormat given. add pageWidth/Height parameters ([#670](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/670), [#676](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/676)) ([0353fac](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/0353facfbe2e1ebcfb14bfc14f46ca83ab3c1301))
+* **GeneratePNGs:** can generate PNGs browserless by node script, improved speed ([#670](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/670)) ([4089a59](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/4089a59c555750866487e147dfb6a20a95711cf4))
+* **PageFormat:** warn if a page can't fit a single MusicSystem. ([1483403](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/148340303f2589d96bbdad16f52d406c3655b579))
+* **Rendering:** add option renderSingleHorizontalStaffline ([#681](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/681)) ([b1c298d](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/b1c298d55e56b6a0a1509d349dec4ca1205046fd))
+* **Rendering:** add osmd.Drawer.DrawOverlayLine, which allows the user to render colored lines on any MusicPage ([#651](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/651)) ([6d8b9fc](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/6d8b9fc03e1d11f2cc622d1e30f43629d63a14f6))
+* **Testing:** add visual regression testing script, generating diffs for all OSMD samples ([c17a3c7](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/c17a3c72847dce0e8aef3caf49a9f9a9e8d52bc9))
+
+
+
+## [0.7.3](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.7.2...0.7.3) (2020-01-15)
+
+
+### Bug Fixes
+
+* **Arpeggio:** fix up/down direction (wrong in Vexflow), remove Vexflow dependency ([450b2d9](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/450b2d9)), closes [#645](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/645)
+* **Dynamics, drawing range:** fix crescendo crashing when partially out of drawing range ([#644](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/644)) ([8105270](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/8105270))
+
+### Etc
+* **Imports:** Remove many Vexflow dependencies in core OSMD classes (Arpeggio, MusicSheetCalculator, other /Graphical/ classes) ([450b2d9](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/450b2d91bb4d52a60aeb6fa3425865e58efffebc), [90d93b9](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/90d93b907315b8b1d93586d4849d96c41fb60661))
+* **Cursor:** Improve Follow Cursor performance (thanks to @praisethemoon) ([#639](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/pull/639))
+
+
+
+## [0.7.2](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.7.1...0.7.2) (2019-12-13)
+
+
+### Bug Fixes
+
+* **Arpeggio:** don't draw one-note arpeggios ([#617](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/617)) ([5f7e183](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/5f7e183))
+* **Arpeggios:** prevent infinite height bug, arpeggio always going across voices ([#546](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/546)) ([3fbed99](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/3fbed99))
+* **autoBeam:** don't beam over half notes or beat, prevent separate beams connecting ([da464aa](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/da464aa)), closes [#574](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/574)
+* **autoBeamOption:** don't beam notes of type quarter or longer in tuplets ([c3b3b5a](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/c3b3b5a))
+* **barline:** don't automatically end piece with final barline if not specified ([#569](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/569)) ([8ae7938](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/8ae7938))
+* **barlines:** fix left barline added to end barline ([#588](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/588)) ([6608f17](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/6608f17))
+* **ChordSymbols:** save and show all chords on single note ([#599](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/599)) ([2d7e265](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/2d7e265))
+* **credits placement:** fix title and composer label placement, now in relation to Staffline width ([b7af9b8](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/b7af9b8)), closes [#578](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/578)
+* **Cursor:** starts and ends at selected range of measures to draw ([#566](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/566)) ([3fe770e](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/3fe770e))
+* **demo:** set and reset options for specific test samples correctly ([b28b5dc](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/b28b5dc))
+* **empty measures:** not filled with whole rests by default anymore. new option fillEmptyMeasuresWithWholeRest ([#625](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/625)) ([00522db](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/00522db))
+* **exports:** export Vexflow graphical classes (e.g. VexflowGraphicalNote) ([06ef2f3](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/06ef2f3)), closes [#549](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/549)
+* **Fractions:** add gt and gte methods, replace some > and < occurences ([#518](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/518)) ([c80fea6](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/c80fea6))
+* **lyrics Placement:** fix support for Sibelius format ([#583](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/583)) ([084f308](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/084f308))
+* **octaveShift, drawRange:** start and end at within draw range, fix error ([9f6bb82](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/9f6bb82)), closes [#586](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/586)
+* **SkyBottomLineCalculator:** prevent infinity bug with startIndex = endIndex and .slice ([554d277](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/554d277)), closes [#575](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/575)
+* **stemColor:** respect ColoringEnabled, re-color to default if disabled ([#614](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/614)) ([52928cb](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/52928cb))
+* **whole rest bbox:** fix whole rest bounding box in non-4/4 time ([#609](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/609)) ([2b91655](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/2b91655)), closes [#616](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/616) [#605](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/605)
 
 
+### Features
+
+* **alignRests:** add auto option (alignRests: 2) which only aligns rests if measure contains simultaneous voices ([1c8de9f](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/1c8de9f)), closes [#621](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/621)
+* **API:** Allow updating the graphical sheet from the music sheet ([#622](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/622)) ([55c3d8a](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/55c3d8a))
+* **API:** save ActiveTimeSignature in SourceMeasure ([4927727](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/4927727))
+* **barline:** able to not draw barlines (fix barline none) ([#391](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/391)) ([7524287](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/7524287))
+* **ChordSymbols:** show all chordsymbols over a single note/staffEntry (spacing WIP) ([#599](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/599)) ([6eb97fa](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/6eb97fa))
+* **exports:** Add module exports for VoiceData ([#631](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/631)) ([a2ce3a2](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/a2ce3a2))
+* **open same-origin xml url:** supported ([#603](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/603)) ([0ac0132](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/0ac0132)), closes [#576](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/576)
+* **options:** add option to (not) draw slurs ([#602](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/602)) ([1333195](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/1333195))
+* **options:** add option to align rests and avoid rest collisions, which also aligns rests with voices ([#621](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/621)) ([ca6d730](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/ca6d730))
+* **options:** can set font family for labels (default Times New Roman) ([35ee9e2](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/35ee9e2)), closes [#477](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/477)
+* **options:** offer options to disable measure numbers, set their interval ([0d5af7a](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/0d5af7a))
+* **Options:** add drawLyrics option ([#602](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/602)) ([9d09586](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/9d09586))
+
+
+
+
+<a name="0.7.1"></a>
+## [0.7.1](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.7.0...0.7.1) (2019-08-19)
+
+
+### Bug Fixes
+
+* **part names:** drawPartNames = false does not leave left x-spacing in first line anymore ([595f8ab](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/595f8ab)), closes [#515](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/515)
+* **stemColor:** respect colorStemsLikeNoteheads option in XML color mode as well ([6548c57](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/6548c57)), closes [#486](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/486)
+* **Ties:** fix not displaying ties because measure to graphicalMeasure dictionary didn't work ([27d3645](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/27d3645)), closes [#503](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/503)
+
+
+### Features
+
+* **drawFromMeasureNumber:** can now specify first measure to draw from as well as last,
+closes [#528](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/528) [#482](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/482)
+* **bar lines:** Add support for double and final bar lines ([#519](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/519)) ([e05b99a](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/e05b99a))
+* **color:** add colorStemsLikeNoteheads option, color beams like stems as well if matching ([b631879](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/b631879)), closes [#486](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/486)
+* **tempo:** Save measures' BPM ([#558](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/558)) ([cf199ad](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/cf199ad)), closes [#557](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/557)
+* **transparency:** add invisible notes as transparent StaveNotes instead of GhostNotes ([d0211a7](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/d0211a7))
+* **transparency:** allow transparency after re-render with Note.PrintObject = false ([52212d6](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/52212d6)), closes [#509](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/509)
+* **demo:** add option to follow cursor (, add function test selectMeasuresToDraw)
+
+
+
+<a name="0.7.0"></a>
+# [0.7.0](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.6.8...0.7.0) (2019-03-25)
+
+
+### Bug Fixes
+
+* **Ajax Timeout:** Ajax timeout was not handled for IE and node ([2a66245](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/2a66245)), closes [#479](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/479)
+* **Octava display error:** Fixed the octave shift bracket to be shown over the note line ([0e7f9f7](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/0e7f9f7)), closes [#490](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/490)
+* **options:** fix drawing parameters always being created in setOptions, causing some options to be reset with {} arguments ([419c39d](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/419c39d))
+* **partAbbreviations:** don't draw part abbreviations for solo parts, add option to not draw abbreviations ([945d6a1](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/945d6a1))
+* **partNames:** fix showing only one of multiple parts with same instrument name ([3bee67e](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/3bee67e))
+* **ties:** fix error in tie handling when no end note found ([3757db5](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/3757db5))
+* **tremolo:** enable half note tremolo between two different half notes. ([eefadf8](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/eefadf8)), closes [#472](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/472)
+* **tuplet:** correct tuplet label for number of notes not equal to tuplet label ([bf5aaa1](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/bf5aaa1)), closes [#485](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/485)
+* **tuplet:** fix duplet, quadruplet, tuplet layout ([596e794](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/596e794)), closes [#488](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/488)
+
+
+### Features
+
+* **Coloring:** Add automatic Boomwhacker coloring scheme ([#494](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/494)) ([adaecc4](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/adaecc4)), closes [#486](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/486)
+* **Instrument label abbreviations:** Add instrument label abbreviations ([a67dc00](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/a67dc00)), closes [#466](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/466)
+* **Types:** OSMD uses TS types from Definitely typed now, improved Vexflow compatibility in TS ([53f2c44](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/53f2c44))
+* **Options:** implement DrawCredits ([8c6df97](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/8c6df97))
+* **Parts:** read and display XML part abbreviations (e.g. Vln for Violin) ([9fe031e](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/9fe031e))
+* **Tremolo:** display single note tremolos ([db1840c](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/db1840c)), closes [#431](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/431)
+
+
+
+## [0.6.8](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.6.6...0.6.8) (2019-02-08)
+
+### Bug Fixes
+
+* **alignment:** fix alignment of notes following dotted rest ([a7eb53d](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/a7eb53d)), closes [#484](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/484)
 
 ## [0.6.7](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.6.6...0.6.7) (2019-01-16)
 

+ 7 - 17
LICENSE

@@ -1,21 +1,11 @@
-The MIT License (MIT)
+Copyright 2019 PhonicScore
 
-Copyright (c) 2016 PhonicScore
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 12 - 18
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
@@ -18,7 +18,7 @@ MusicXML is the de facto standard for sharing sheet music on the internet. VexFl
 
 OpenSheetMusicDisplay brings the two together and offers an open source turnkey solution for your digital sheet music project.
 
-Written in [TypeScript](https://www.typescriptlang.org) and released under [MIT license](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/blob/develop/LICENSE).
+Written in [TypeScript](https://www.typescriptlang.org) and released under [BSD license](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/blob/develop/LICENSE).
 
 Try the [Demo](https://opensheetmusicdisplay.github.io/demo/) to see what OSMD can do.
 
@@ -34,21 +34,15 @@ To contact us directly, you can use the [Contact form on opensheetmusicdisplay.o
 or<br>
 [join the chat on Gitter](https://gitter.im/opensheetmusicdisplay/opensheetmusicdisplay).
 
-If you'd like to support OSMD and our ongoing work, you can donate via PayPal:<br>
+<a href="https://github.com/sponsors/opensheetmusicdisplay/" alt="OSMD on Github Sponsors">
+<img src="https://user-images.githubusercontent.com/33069673/104042293-99ccfa80-51da-11eb-9dc9-fac075a33224.png" height="200" alt="OSMD on Github Sponsors">
+</a><br>
+If you'd like to support OSMD and our ongoing work, and receive additional rewards like early builds (Audio Player, Transposition plugin) or a postcard from Vienna,
+you can support us via Github Sponsors:<br>
+https://github.com/sponsors/opensheetmusicdisplay/
+<br>
+<br>
+Though we highly recommend the sponsor route, you can also donate via Paypal:<br>
+
 [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FPHCYVV2HH8VU)<br>
 Any support is highly appreciated.
-
-
-
-
-<!--# <a name="license"></a>License
-The MIT License (MIT)
-
-Copyright &copy; 2016 PhonicScore GmbH
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--->

+ 3033 - 0
demo/BrahWiMeSample.musicxml

@@ -0,0 +1,3033 @@
+<?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">
+  <movement-title>Wie Melodien zieht es mir (Page 1)</movement-title>
+  <identification>
+    <creator type="composer">Johannes Brahms</creator>
+    <creator type="lyricist">Klaus Groth</creator>
+    <rights>Copyright © 2002 MakeMusic, Inc.</rights>
+    <encoding>
+      <software>Finale v25 for Mac</software>
+      <encoding-date>2017-12-12</encoding-date>
+      <supports attribute="new-system" element="print" type="yes" value="yes"/>
+      <supports attribute="new-page" element="print" type="yes" value="yes"/>
+      <supports element="accidental" type="yes"/>
+      <supports element="beam" type="yes"/>
+      <supports element="stem" type="yes"/>
+    </encoding>
+  </identification>
+  <defaults>
+    <scaling>
+      <millimeters>6.35</millimeters>
+      <tenths>40</tenths>
+    </scaling>
+    <page-layout>
+      <page-height>1760</page-height>
+      <page-width>1360</page-width>
+      <page-margins type="both">
+        <left-margin>80</left-margin>
+        <right-margin>80</right-margin>
+        <top-margin>80</top-margin>
+        <bottom-margin>80</bottom-margin>
+      </page-margins>
+    </page-layout>
+    <system-layout>
+      <system-margins>
+        <left-margin>0</left-margin>
+        <right-margin>0</right-margin>
+      </system-margins>
+      <system-distance>130</system-distance>
+      <top-system-distance>70</top-system-distance>
+    </system-layout>
+    <staff-layout>
+      <staff-distance>80</staff-distance>
+    </staff-layout>
+    <appearance>
+      <line-width type="stem">0.8333</line-width>
+      <line-width type="beam">5</line-width>
+      <line-width type="staff">1.25</line-width>
+      <line-width type="light barline">1.875</line-width>
+      <line-width type="heavy barline">5</line-width>
+      <line-width type="leger">1.875</line-width>
+      <line-width type="ending">0.8333</line-width>
+      <line-width type="wedge">1.25</line-width>
+      <line-width type="enclosure">1.25</line-width>
+      <line-width type="tuplet bracket">0.8333</line-width>
+      <note-size type="grace">60</note-size>
+      <note-size type="cue">60</note-size>
+      <distance type="hyphen">60</distance>
+      <distance type="beam">8</distance>
+    </appearance>
+    <music-font font-family="Maestro,engraved" font-size="18"/>
+    <word-font font-family="Times New Roman" font-size="9"/>
+    <lyric-font font-family="Times New Roman" font-size="10"/>
+  </defaults>
+  <credit page="1">
+    <credit-type>page number</credit-type>
+    <credit-words default-x="80" default-y="1680" font-size="12" valign="top">2</credit-words>
+  </credit>
+  <credit page="1">
+    <credit-type>rights</credit-type>
+    <credit-words default-x="680" default-y="80" font-size="9" halign="center" valign="bottom">Copyright © 2002 MakeMusic, Inc.</credit-words>
+  </credit>
+  <credit page="1">
+    <credit-type>title</credit-type>
+    <credit-words default-x="680" default-y="1640" font-size="24" justify="center" valign="top">Wie Melodien zieht es mir</credit-words>
+  </credit>
+  <credit page="1">
+    <credit-words default-x="680" default-y="1580" font-size="14" justify="center" valign="top">Op. 105, No. 1</credit-words>
+  </credit>
+  <credit page="1">
+    <credit-type>lyricist</credit-type>
+    <credit-words default-x="80" default-y="1500" font-size="10" valign="bottom">Klaus Groth</credit-words>
+  </credit>
+  <credit page="1">
+    <credit-type>composer</credit-type>
+    <credit-words default-x="1280" default-y="1500" font-size="10" halign="right" valign="bottom">Johannes Brahms</credit-words>
+  </credit>
+  <part-list>
+    <score-part id="P1">
+      <part-name>Voice</part-name>
+      <score-instrument id="P1-I1">
+        <instrument-name>Voice</instrument-name>
+        <instrument-sound>voice.vocals</instrument-sound>
+        <solo/>
+      </score-instrument>
+      <midi-instrument id="P1-I1">
+        <midi-channel>1</midi-channel>
+        <midi-program>53</midi-program>
+        <volume>80</volume>
+        <pan>0</pan>
+      </midi-instrument>
+    </score-part>
+    <score-part id="P2">
+      <part-name>Piano</part-name>
+      <score-instrument id="P2-I2">
+        <instrument-name>Acoustic Grand Piano</instrument-name>
+        <instrument-sound>keyboard.piano</instrument-sound>
+      </score-instrument>
+      <midi-instrument id="P2-I2">
+        <midi-channel>2</midi-channel>
+        <midi-program>1</midi-program>
+        <volume>80</volume>
+        <pan>0</pan>
+      </midi-instrument>
+    </score-part>
+  </part-list>
+  <!--=========================================================-->
+  <part id="P1">
+    <measure number="1" width="340">
+      <print page-number="2">
+        <system-layout>
+          <system-margins>
+            <left-margin>120</left-margin>
+            <right-margin>0</right-margin>
+          </system-margins>
+          <top-system-distance>230</top-system-distance>
+        </system-layout>
+        <measure-numbering>system</measure-numbering>
+      </print>
+      <attributes>
+        <divisions>2</divisions>
+        <key>
+          <fifths>3</fifths>
+          <mode>major</mode>
+        </key>
+        <time symbol="cut">
+          <beats>2</beats>
+          <beat-type>2</beat-type>
+        </time>
+        <clef>
+          <sign>G</sign>
+          <line>2</line>
+        </clef>
+      </attributes>
+      <direction directive="yes" placement="above">
+        <direction-type>
+          <words default-y="15" font-size="10.5" font-weight="bold">Zart</words>
+        </direction-type>
+        <sound tempo="96"/>
+      </direction>
+      <note default-x="139">
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+      </note>
+      <note default-x="189">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="-14">up</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>single</syllabic>
+          <text>Wie</text>
+        </lyric>
+      </note>
+      <note default-x="239">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="-4">up</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>begin</syllabic>
+          <text>Me</text>
+        </lyric>
+      </note>
+      <note default-x="289">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="11">up</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>middle</syllabic>
+          <text>lo</text>
+        </lyric>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="2" width="220">
+      <note default-x="18">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="-45.5">down</stem>
+        <notations>
+          <slur number="1" placement="above" type="start"/>
+        </notations>
+        <lyric default-y="-80" number="1">
+          <syllabic>middle</syllabic>
+          <text>di</text>
+        </lyric>
+      </note>
+      <note default-x="68">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>5</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="-50.5">down</stem>
+        <notations>
+          <slur number="1" type="stop"/>
+        </notations>
+      </note>
+      <note default-x="118">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="-55.5">down</stem>
+        <notations>
+          <slur number="1" placement="above" type="start"/>
+        </notations>
+        <lyric default-y="-80" number="1">
+          <syllabic>end</syllabic>
+          <text>en</text>
+          <extend/>
+        </lyric>
+      </note>
+      <note default-x="168">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="11">up</stem>
+        <notations>
+          <slur number="1" type="stop"/>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="3" width="217">
+      <note default-x="18">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem default-y="6">up</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>single</syllabic>
+          <text>zieht</text>
+        </lyric>
+      </note>
+      <note default-x="116">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="1">up</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>single</syllabic>
+          <text>es</text>
+        </lyric>
+      </note>
+      <note default-x="166">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="11">up</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>single</syllabic>
+          <text>mir</text>
+        </lyric>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="4" width="303">
+      <note default-x="26">
+        <pitch>
+          <step>F</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <dot/>
+        <accidental>natural</accidental>
+        <stem default-y="1">up</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>begin</syllabic>
+          <text>lei</text>
+        </lyric>
+      </note>
+      <note default-x="132">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>natural</accidental>
+        <stem default-y="6">up</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>end</syllabic>
+          <text>se</text>
+        </lyric>
+      </note>
+      <note default-x="175">
+        <pitch>
+          <step>F</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <dot/>
+        <stem default-y="1">up</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>single</syllabic>
+          <text>durch</text>
+        </lyric>
+      </note>
+      <note default-x="265">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="6">up</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>single</syllabic>
+          <text>den</text>
+        </lyric>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="5" width="371">
+      <print new-system="yes">
+        <system-layout>
+          <system-distance>165</system-distance>
+        </system-layout>
+      </print>
+      <note default-x="107">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem default-y="-4">up</stem>
+        <lyric default-y="-80" number="1" relative-x="3">
+          <syllabic>single</syllabic>
+          <text>Sinn,</text>
+        </lyric>
+      </note>
+      <note default-x="243">
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+      </note>
+      <note default-x="304">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>5</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="-50.5">down</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>single</syllabic>
+          <text>wie</text>
+        </lyric>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="6" width="315">
+      <note default-x="18">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <dot/>
+        <stem default-y="-55.5">down</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>begin</syllabic>
+          <text>Früh</text>
+        </lyric>
+      </note>
+      <note default-x="124">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="1">up</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>middle</syllabic>
+          <text>lings</text>
+        </lyric>
+      </note>
+      <note default-x="168">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <dot/>
+        <stem default-y="6">up</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>middle</syllabic>
+          <text>blu</text>
+        </lyric>
+      </note>
+      <note default-x="274">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-4">up</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>end</syllabic>
+          <text>men</text>
+        </lyric>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="7" width="230">
+      <note default-x="18">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem default-y="-55.5">down</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>single</syllabic>
+          <text>blüht</text>
+        </lyric>
+      </note>
+      <note default-x="123">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="11">up</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>single</syllabic>
+          <text>es</text>
+        </lyric>
+      </note>
+      <note default-x="175">
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="-40.5">down</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>single</syllabic>
+          <text>und</text>
+        </lyric>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="8" width="284">
+      <note default-x="27">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>5</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <dot/>
+        <accidental>sharp</accidental>
+        <stem default-y="-45.5">down</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>single</syllabic>
+          <text>schwebt</text>
+        </lyric>
+      </note>
+      <note default-x="120">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>5</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-50.5">down</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>single</syllabic>
+          <text>wie</text>
+        </lyric>
+      </note>
+      <note default-x="163">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <dot/>
+        <stem default-y="-55.5">down</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>single</syllabic>
+          <text>Duft</text>
+        </lyric>
+      </note>
+      <note default-x="249">
+        <pitch>
+          <step>F</step>
+          <alter>2</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>double-sharp</accidental>
+        <stem default-y="1">up</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>begin</syllabic>
+          <text>da</text>
+        </lyric>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="9" width="296">
+      <print new-system="yes">
+        <system-layout>
+          <system-distance>165</system-distance>
+        </system-layout>
+      </print>
+      <note default-x="113">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem default-y="6">up</stem>
+        <lyric default-y="-80" number="1" relative-x="3">
+          <syllabic>end</syllabic>
+          <text>hin,</text>
+        </lyric>
+      </note>
+      <note default-x="210">
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="10" width="202">
+      <note default-x="16">
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+      </note>
+      <note default-x="118">
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+      </note>
+      <note default-x="159">
+        <pitch>
+          <step>B</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <accidental>sharp</accidental>
+        <stem default-y="-55.5">down</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>single</syllabic>
+          <text>und</text>
+        </lyric>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="11" width="251">
+      <note default-x="39">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>5</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <dot/>
+        <accidental>sharp</accidental>
+        <stem default-y="-45.5">down</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>single</syllabic>
+          <text>schwebt</text>
+        </lyric>
+      </note>
+      <note default-x="108">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>5</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-50.5">down</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>single</syllabic>
+          <text>wie</text>
+        </lyric>
+      </note>
+      <note default-x="151">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <dot/>
+        <stem default-y="11">up</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>single</syllabic>
+          <text>Duft</text>
+        </lyric>
+      </note>
+      <note default-x="221">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>sharp</accidental>
+        <stem default-y="-9">up</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>begin</syllabic>
+          <text>da</text>
+        </lyric>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="12" width="220">
+      <note default-x="24">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem default-y="-4">up</stem>
+        <lyric default-y="-80" number="1">
+          <syllabic>end</syllabic>
+          <text>hin.</text>
+        </lyric>
+      </note>
+      <note default-x="120">
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="13" width="231">
+      <note>
+        <rest measure="yes"/>
+        <duration>8</duration>
+        <voice>1</voice>
+      </note>
+    </measure>
+  </part>
+  <!--=========================================================-->
+  <part id="P2">
+    <measure number="1" width="340">
+      <print>
+        <staff-layout number="1">
+          <staff-distance>100</staff-distance>
+        </staff-layout>
+        <staff-layout number="2">
+          <staff-distance>60</staff-distance>
+        </staff-layout>
+        <measure-numbering>none</measure-numbering>
+      </print>
+      <attributes>
+        <divisions>8</divisions>
+        <key>
+          <fifths>3</fifths>
+          <mode>major</mode>
+        </key>
+        <time symbol="cut">
+          <beats>2</beats>
+          <beat-type>2</beat-type>
+        </time>
+        <staves>2</staves>
+        <clef number="1">
+          <sign>F</sign>
+          <line>4</line>
+        </clef>
+        <clef number="2">
+          <sign>F</sign>
+          <line>4</line>
+        </clef>
+      </attributes>
+      <direction placement="below">
+        <direction-type>
+          <dynamics default-y="-80" halign="left">
+            <p/>
+          </dynamics>
+        </direction-type>
+        <staff>1</staff>
+        <sound dynamics="54"/>
+      </direction>
+      <note default-x="139">
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <staff>1</staff>
+      </note>
+      <direction placement="below">
+        <direction-type>
+          <words default-y="-80" font-style="italic" relative-x="14">sempre dolce</words>
+        </direction-type>
+        <staff>1</staff>
+      </direction>
+      <note default-x="164">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-50">down</stem>
+        <staff>1</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <slur default-x="7" default-y="14" number="1" placement="above" type="start"/>
+        </notations>
+      </note>
+      <note default-x="189">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-48">down</stem>
+        <staff>1</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="214">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-45">down</stem>
+        <staff>1</staff>
+        <beam number="1">end</beam>
+      </note>
+      <note default-x="239">
+        <pitch>
+          <step>A</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-55">down</stem>
+        <staff>1</staff>
+        <beam number="1">begin</beam>
+      </note>
+      <note default-x="264">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-60.5">down</stem>
+        <staff>1</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="289">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="36">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="313">
+        <pitch>
+          <step>A</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="30">up</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <notations>
+          <slur number="1" type="stop"/>
+        </notations>
+      </note>
+      <backup>
+        <duration>32</duration>
+      </backup>
+      <note default-x="139">
+        <pitch>
+          <step>A</step>
+          <octave>1</octave>
+        </pitch>
+        <duration>16</duration>
+        <voice>3</voice>
+        <type>half</type>
+        <stem default-y="1">up</stem>
+        <staff>2</staff>
+      </note>
+      <note default-x="139">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>16</duration>
+        <voice>3</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <staff>2</staff>
+      </note>
+      <note default-x="239">
+        <rest/>
+        <duration>8</duration>
+        <voice>3</voice>
+        <type>quarter</type>
+        <staff>2</staff>
+      </note>
+      <forward>
+        <duration>8</duration>
+        <voice>3</voice>
+        <staff>2</staff>
+      </forward>
+    </measure>
+    <!--=======================================================-->
+    <measure number="2" width="220">
+      <note default-x="18">
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <staff>1</staff>
+      </note>
+      <note default-x="43">
+        <pitch>
+          <step>A</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-35">down</stem>
+        <staff>1</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <slur default-x="7" default-y="20" number="1" placement="above" type="start"/>
+        </notations>
+      </note>
+      <note default-x="68">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-32">down</stem>
+        <staff>1</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="93">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-30">down</stem>
+        <staff>1</staff>
+        <beam number="1">end</beam>
+      </note>
+      <note default-x="118">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-55">down</stem>
+        <staff>1</staff>
+        <beam number="1">begin</beam>
+      </note>
+      <note default-x="143">
+        <pitch>
+          <step>A</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-60.5">down</stem>
+        <staff>1</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="168">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="36">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="193">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="30">up</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <notations>
+          <slur number="1" type="stop"/>
+        </notations>
+      </note>
+      <backup>
+        <duration>32</duration>
+      </backup>
+      <note default-x="18">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>16</duration>
+        <voice>3</voice>
+        <type>half</type>
+        <stem default-y="-19">up</stem>
+        <staff>2</staff>
+      </note>
+      <note default-x="119">
+        <rest/>
+        <duration>8</duration>
+        <voice>3</voice>
+        <type>quarter</type>
+        <staff>2</staff>
+      </note>
+      <forward>
+        <duration>8</duration>
+        <voice>3</voice>
+        <staff>2</staff>
+      </forward>
+    </measure>
+    <!--=======================================================-->
+    <measure number="3" width="217">
+      <forward>
+        <duration>32</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+      </forward>
+      <backup>
+        <duration>32</duration>
+      </backup>
+      <note default-x="18">
+        <pitch>
+          <step>D</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="29">up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <slur number="1" placement="below" type="start"/>
+        </notations>
+      </note>
+      <note default-x="43">
+        <pitch>
+          <step>A</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="35">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="68">
+        <pitch>
+          <step>D</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="41">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        <notations>
+          <slur default-x="6" default-y="-37" number="1" type="stop"/>
+        </notations>
+      </note>
+      <note default-x="92">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="-61">down</stem>
+        <staff>1</staff>
+        <beam number="1">end</beam>
+      </note>
+      <note default-x="92">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="116">
+        <pitch>
+          <step>D</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="29">up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <slur number="1" placement="below" type="start"/>
+        </notations>
+      </note>
+      <note default-x="141">
+        <pitch>
+          <step>D</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="35">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="166">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="41">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        <notations>
+          <slur default-x="6" default-y="-26" number="1" type="stop"/>
+        </notations>
+      </note>
+      <note default-x="191">
+        <pitch>
+          <step>A</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="-61">down</stem>
+        <staff>1</staff>
+        <beam number="1">end</beam>
+      </note>
+      <note default-x="191">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+        <staff>1</staff>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="4" width="303">
+      <forward>
+        <duration>32</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+      </forward>
+      <backup>
+        <duration>32</duration>
+      </backup>
+      <note default-x="26">
+        <pitch>
+          <step>D</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="29.5">up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <slur number="1" placement="below" type="start"/>
+        </notations>
+      </note>
+      <note default-x="62">
+        <pitch>
+          <step>B</step>
+          <alter>-1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <accidental>flat</accidental>
+        <stem default-y="35">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="90">
+        <pitch>
+          <step>D</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="39.5">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        <notations>
+          <slur default-x="8" default-y="-37" number="1" type="stop"/>
+        </notations>
+      </note>
+      <note default-x="132">
+        <pitch>
+          <step>F</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <accidental>natural</accidental>
+        <stem default-y="-60.5">down</stem>
+        <staff>1</staff>
+        <beam number="1">end</beam>
+      </note>
+      <note default-x="132">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <alter>-1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <accidental>flat</accidental>
+        <stem>down</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="175">
+        <pitch>
+          <step>D</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="29">up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <slur number="1" placement="below" type="start"/>
+        </notations>
+      </note>
+      <note default-x="204">
+        <pitch>
+          <step>D</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="34.5">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="237">
+        <pitch>
+          <step>F</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <accidental>natural</accidental>
+        <stem default-y="41">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        <notations>
+          <slur default-x="6" default-y="-26" number="1" type="stop"/>
+        </notations>
+      </note>
+      <note default-x="265">
+        <pitch>
+          <step>B</step>
+          <alter>-1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="-60.5">down</stem>
+        <staff>1</staff>
+        <beam number="1">end</beam>
+      </note>
+      <note default-x="265">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+        <staff>1</staff>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="5" width="371">
+      <print new-system="yes">
+        <staff-layout number="2">
+          <staff-distance>70</staff-distance>
+        </staff-layout>
+      </print>
+      <forward>
+        <duration>16</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+      </forward>
+      <attributes>
+        <clef number="1">
+          <sign>G</sign>
+          <line>2</line>
+        </clef>
+      </attributes>
+      <note default-x="243">
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <staff>1</staff>
+      </note>
+      <note default-x="271">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-4">up</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="271">
+        <chord/>
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="304">
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <staff>1</staff>
+      </note>
+      <note default-x="337">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="11">up</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="337">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>1</staff>
+      </note>
+      <backup>
+        <duration>32</duration>
+      </backup>
+      <note default-x="107">
+        <pitch>
+          <step>A</step>
+          <octave>1</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="34">up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <slur number="1" placement="below" type="start"/>
+        </notations>
+      </note>
+      <note default-x="136">
+        <pitch>
+          <step>A</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="40">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="164">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="46">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        <notations>
+          <slur default-x="6" default-y="-35" number="1" type="stop"/>
+        </notations>
+      </note>
+      <note default-x="194">
+        <pitch>
+          <step>A</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="-66">down</stem>
+        <staff>1</staff>
+        <beam number="1">end</beam>
+      </note>
+      <note default-x="194">
+        <chord/>
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="243">
+        <pitch>
+          <step>A</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="-35.5">down</stem>
+        <staff>2</staff>
+      </note>
+      <note default-x="271">
+        <rest/>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <staff>2</staff>
+      </note>
+      <note default-x="304">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="11">up</stem>
+        <staff>2</staff>
+      </note>
+      <note default-x="337">
+        <rest/>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <staff>2</staff>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="6" width="315">
+      <note default-x="18">
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <staff>1</staff>
+      </note>
+      <note default-x="52">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <tie type="start"/>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-19">up</stem>
+        <staff>1</staff>
+        <notations>
+          <tied type="start"/>
+        </notations>
+      </note>
+      <note default-x="96">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>8</duration>
+        <tie type="stop"/>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="1">up</stem>
+        <staff>1</staff>
+        <notations>
+          <tied type="stop"/>
+        </notations>
+      </note>
+      <note default-x="96">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>8</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="168">
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <staff>1</staff>
+      </note>
+      <note default-x="202">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <tie type="start"/>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-19">up</stem>
+        <staff>1</staff>
+        <notations>
+          <tied type="start"/>
+        </notations>
+      </note>
+      <note default-x="247">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>8</duration>
+        <tie type="stop"/>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="-4">up</stem>
+        <staff>1</staff>
+        <notations>
+          <tied type="stop"/>
+        </notations>
+      </note>
+      <note default-x="247">
+        <chord/>
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>8</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        <staff>1</staff>
+      </note>
+      <backup>
+        <duration>32</duration>
+      </backup>
+      <note default-x="18">
+        <pitch>
+          <step>D</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>12</duration>
+        <voice>3</voice>
+        <type>quarter</type>
+        <dot/>
+        <stem default-y="-19">up</stem>
+        <staff>2</staff>
+        <notations>
+          <slur number="1" placement="above" type="start"/>
+        </notations>
+      </note>
+      <note default-x="124">
+        <pitch>
+          <step>D</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="-55.5">down</stem>
+        <staff>2</staff>
+      </note>
+      <note default-x="168">
+        <pitch>
+          <step>D</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>12</duration>
+        <voice>3</voice>
+        <type>quarter</type>
+        <dot/>
+        <stem default-y="-19">up</stem>
+        <staff>2</staff>
+      </note>
+      <note default-x="274">
+        <pitch>
+          <step>D</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="-55.5">down</stem>
+        <staff>2</staff>
+        <notations>
+          <slur number="1" type="stop"/>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="7" width="230">
+      <note default-x="18">
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <staff>1</staff>
+      </note>
+      <note default-x="44">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-5">up</stem>
+        <staff>1</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <slur number="1" placement="below" type="start"/>
+        </notations>
+      </note>
+      <note default-x="70">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-5">up</stem>
+        <staff>1</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="96">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-5">up</stem>
+        <staff>1</staff>
+        <beam number="1">end</beam>
+      </note>
+      <note default-x="123">
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <staff>1</staff>
+      </note>
+      <note default-x="175">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>5</octave>
+        </pitch>
+        <duration>8</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="-50.5">down</stem>
+        <staff>1</staff>
+        <notations>
+          <slur number="2" placement="above" type="start"/>
+        </notations>
+      </note>
+      <note default-x="175">
+        <chord/>
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+        </pitch>
+        <duration>8</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <staff>1</staff>
+      </note>
+      <backup>
+        <duration>32</duration>
+      </backup>
+      <note default-x="18">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>16</duration>
+        <voice>3</voice>
+        <type>half</type>
+        <stem default-y="-19">up</stem>
+        <staff>2</staff>
+      </note>
+      <note default-x="123">
+        <pitch>
+          <step>A</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="35">up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+      </note>
+      <note default-x="149">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="33.5">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="175">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="32">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="201">
+        <pitch>
+          <step>A</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="30">up</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <notations>
+          <slur number="1" type="stop"/>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="8" width="284">
+      <note default-x="27">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>12</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <dot/>
+        <stem default-y="-55.5">down</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="27">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>5</octave>
+        </pitch>
+        <duration>12</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <dot/>
+        <accidental>sharp</accidental>
+        <stem>down</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="120">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-58">down</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="120">
+        <chord/>
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>5</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="163">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>12</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <dot/>
+        <stem default-y="16">up</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="163">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>12</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <dot/>
+        <stem>up</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="249">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>sharp</accidental>
+        <stem default-y="1">up</stem>
+        <staff>1</staff>
+        <notations>
+          <slur number="2" type="stop"/>
+        </notations>
+      </note>
+      <note default-x="249">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>2</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>double-sharp</accidental>
+        <stem>up</stem>
+        <staff>1</staff>
+      </note>
+      <backup>
+        <duration>32</duration>
+      </backup>
+      <note default-x="27">
+        <pitch>
+          <step>B</step>
+          <octave>1</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="10">up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <slur number="1" placement="below" type="start"/>
+        </notations>
+      </note>
+      <note default-x="56">
+        <pitch>
+          <step>B</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="12">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="91">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <accidental>sharp</accidental>
+        <stem default-y="15">up</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <notations>
+          <slur default-x="6" default-y="-36" number="1" type="stop"/>
+        </notations>
+      </note>
+      <note default-x="120">
+        <rest/>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <staff>2</staff>
+      </note>
+      <note default-x="163">
+        <rest/>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <staff>2</staff>
+      </note>
+      <note default-x="191">
+        <pitch>
+          <step>B</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="-65">down</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <slur default-x="6" default-y="-15" number="1" placement="above" type="start"/>
+        </notations>
+      </note>
+      <note default-x="220">
+        <pitch>
+          <step>A</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="-60">down</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <notations>
+          <slur number="1" type="stop"/>
+        </notations>
+      </note>
+      <note default-x="249">
+        <rest/>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <staff>2</staff>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="9" width="296">
+      <print new-system="yes">
+        <staff-layout number="2">
+          <staff-distance>90</staff-distance>
+        </staff-layout>
+      </print>
+      <note default-x="113">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>8</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="6">up</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="113">
+        <chord/>
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>8</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="159">
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+        </pitch>
+        <duration>16</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem default-y="-40.5">down</stem>
+        <staff>1</staff>
+        <notations>
+          <slur number="1" placement="above" type="start"/>
+        </notations>
+      </note>
+      <note default-x="159">
+        <chord/>
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>5</octave>
+        </pitch>
+        <duration>16</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>down</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="253">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>5</octave>
+        </pitch>
+        <duration>8</duration>
+        <tie type="start"/>
+        <voice>1</voice>
+        <type>quarter</type>
+        <accidental>sharp</accidental>
+        <stem default-y="-45.5">down</stem>
+        <staff>1</staff>
+        <notations>
+          <tied type="start"/>
+        </notations>
+      </note>
+      <note default-x="253">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>5</octave>
+        </pitch>
+        <duration>8</duration>
+        <tie type="start"/>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        <notations>
+          <tied type="start"/>
+        </notations>
+      </note>
+      <backup>
+        <duration>32</duration>
+      </backup>
+      <note default-x="113">
+        <pitch>
+          <step>E</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="35">up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <slur number="2" placement="above" type="start"/>
+        </notations>
+      </note>
+      <note default-x="136">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="37">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="159">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="38.5">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="182">
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="40">up</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+      </note>
+      <note default-x="212">
+        <pitch>
+          <step>B</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <accidental>sharp</accidental>
+        <stem default-y="-30.5">down</stem>
+        <staff>2</staff>
+      </note>
+      <note default-x="235">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>8</duration>
+        <voice>3</voice>
+        <type>quarter</type>
+        <stem default-y="-40.5">down</stem>
+        <staff>2</staff>
+      </note>
+      <note default-x="272">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="-50.5">down</stem>
+        <staff>2</staff>
+        <notations>
+          <slur number="2" type="stop"/>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="10" width="202">
+      <note default-x="18">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>5</octave>
+        </pitch>
+        <duration>8</duration>
+        <tie type="stop"/>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="-45.5">down</stem>
+        <staff>1</staff>
+        <notations>
+          <tied type="stop"/>
+        </notations>
+      </note>
+      <note default-x="18">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>5</octave>
+        </pitch>
+        <duration>8</duration>
+        <tie type="stop"/>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        <notations>
+          <tied type="stop"/>
+        </notations>
+      </note>
+      <note default-x="63">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>5</octave>
+        </pitch>
+        <duration>16</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem default-y="-50.5">down</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="63">
+        <chord/>
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+        </pitch>
+        <duration>16</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>down</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="159">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>8</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="16">up</stem>
+        <staff>1</staff>
+        <notations>
+          <slur number="1" type="stop"/>
+        </notations>
+      </note>
+      <note default-x="159">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>8</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <accidental>sharp</accidental>
+        <stem>up</stem>
+        <staff>1</staff>
+      </note>
+      <backup>
+        <duration>32</duration>
+      </backup>
+      <note default-x="18">
+        <pitch>
+          <step>E</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="-85">down</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <slur bezier-x="25" bezier-y="74" default-x="6" default-y="-25" number="1" placement="above" type="start"/>
+        </notations>
+      </note>
+      <note default-x="41">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="-83">down</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="63">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="-81.5">down</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="86">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="-80">down</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+      </note>
+      <note default-x="118">
+        <pitch>
+          <step>D</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <accidental>natural</accidental>
+        <stem default-y="-20.5">down</stem>
+        <staff>2</staff>
+      </note>
+      <note default-x="141">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>8</duration>
+        <voice>3</voice>
+        <type>quarter</type>
+        <stem default-y="-40.5">down</stem>
+        <staff>2</staff>
+      </note>
+      <note default-x="177">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="-50.5">down</stem>
+        <staff>2</staff>
+        <notations>
+          <slur bezier-x="-42" bezier-y="44" default-x="6" default-y="5" number="1" type="stop"/>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="11" width="251">
+      <note default-x="39">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>12</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <dot/>
+        <accidental>natural</accidental>
+        <stem default-y="-55.5">down</stem>
+        <staff>1</staff>
+        <notations>
+          <slur number="1" placement="above" type="start"/>
+        </notations>
+      </note>
+      <note default-x="39">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>5</octave>
+        </pitch>
+        <duration>12</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <dot/>
+        <accidental>sharp</accidental>
+        <stem>down</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="108">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-58">down</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="108">
+        <chord/>
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>5</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="151">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>12</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <dot/>
+        <stem default-y="11">up</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="151">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>12</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <dot/>
+        <stem>up</stem>
+        <staff>1</staff>
+      </note>
+      <note default-x="221">
+        <pitch>
+          <step>A</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-9">up</stem>
+        <staff>1</staff>
+        <notations>
+          <slur number="1" type="stop"/>
+        </notations>
+      </note>
+      <note default-x="221">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>sharp</accidental>
+        <stem>up</stem>
+        <staff>1</staff>
+      </note>
+      <backup>
+        <duration>32</duration>
+      </backup>
+      <note default-x="39">
+        <pitch>
+          <step>A</step>
+          <octave>1</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="20">up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <slur bezier-x="30" bezier-y="1" default-x="7" default-y="-82" number="1" placement="below" type="start"/>
+        </notations>
+      </note>
+      <note default-x="62">
+        <pitch>
+          <step>A</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="22">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="85">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="23.5">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="108">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="25">up</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <notations>
+          <slur bezier-x="-6" bezier-y="-30" default-x="7" default-y="-24" number="1" type="stop"/>
+        </notations>
+      </note>
+      <note default-x="151">
+        <rest/>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <staff>2</staff>
+      </note>
+      <note default-x="175">
+        <pitch>
+          <step>B</step>
+          <octave>1</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="20">up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <slur bezier-x="24" bezier-y="-1" default-x="7" default-y="-77" number="1" placement="below" type="start"/>
+        </notations>
+      </note>
+      <note default-x="198">
+        <pitch>
+          <step>B</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="23">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="221">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="25">up</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <notations>
+          <slur bezier-x="-2" bezier-y="-25" default-x="6" default-y="-35" number="1" type="stop"/>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="12" width="220">
+      <forward>
+        <duration>16</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+      </forward>
+      <forward>
+        <duration>8</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+      </forward>
+      <direction placement="below">
+        <direction-type>
+          <wedge default-y="-73" type="crescendo"/>
+        </direction-type>
+        <offset>-1</offset>
+        <staff>1</staff>
+      </direction>
+      <note default-x="168">
+        <rest>
+          <display-step>B</display-step>
+          <display-octave>4</display-octave>
+        </rest>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <staff>1</staff>
+      </note>
+      <direction>
+        <direction-type>
+          <wedge spread="12" type="stop"/>
+        </direction-type>
+        <offset>2</offset>
+        <staff>1</staff>
+      </direction>
+      <note default-x="191">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="6">up</stem>
+        <staff>1</staff>
+        <notations>
+          <slur number="1" placement="above" type="start"/>
+        </notations>
+      </note>
+      <backup>
+        <duration>32</duration>
+      </backup>
+      <note default-x="24">
+        <rest>
+          <display-step>B</display-step>
+          <display-octave>4</display-octave>
+        </rest>
+        <duration>4</duration>
+        <voice>2</voice>
+        <type>eighth</type>
+        <staff>1</staff>
+      </note>
+      <note default-x="47">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>2</voice>
+        <type>eighth</type>
+        <stem default-y="-75">down</stem>
+        <staff>1</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <slur bezier-x="19" bezier-y="45" default-x="6" default-y="-7" number="2" placement="above" type="start"/>
+        </notations>
+      </note>
+      <note default-x="76">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>2</voice>
+        <type>eighth</type>
+        <accidental>natural</accidental>
+        <stem default-y="-72">down</stem>
+        <staff>1</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="98">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>2</voice>
+        <type>eighth</type>
+        <stem default-y="-70">down</stem>
+        <staff>1</staff>
+        <beam number="1">end</beam>
+      </note>
+      <note default-x="122">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>2</voice>
+        <type>eighth</type>
+        <stem default-y="-80">down</stem>
+        <staff>1</staff>
+        <beam number="1">begin</beam>
+      </note>
+      <note default-x="145">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>2</voice>
+        <type>eighth</type>
+        <stem default-y="-84">down</stem>
+        <staff>1</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="168">
+        <pitch>
+          <step>D</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>2</voice>
+        <type>eighth</type>
+        <stem default-y="44.5">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="191">
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>2</voice>
+        <type>eighth</type>
+        <stem default-y="40">up</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+      </note>
+      <backup>
+        <duration>32</duration>
+      </backup>
+      <note default-x="24">
+        <pitch>
+          <step>E</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="25">up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <slur number="3" placement="below" type="start"/>
+        </notations>
+      </note>
+      <note default-x="47">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="27">up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="76">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <stem default-y="30">up</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <notations>
+          <slur number="3" type="stop"/>
+        </notations>
+      </note>
+      <note default-x="98">
+        <rest/>
+        <duration>4</duration>
+        <voice>3</voice>
+        <type>eighth</type>
+        <staff>2</staff>
+      </note>
+      <note default-x="122">
+        <rest/>
+        <duration>8</duration>
+        <voice>3</voice>
+        <type>quarter</type>
+        <staff>2</staff>
+      </note>
+      <forward>
+        <duration>8</duration>
+        <voice>3</voice>
+        <staff>2</staff>
+      </forward>
+    </measure>
+    <!--=======================================================-->
+    <measure number="13" width="231">
+      <direction placement="below">
+        <direction-type>
+          <wedge default-y="-73" spread="12" type="diminuendo"/>
+        </direction-type>
+        <staff>1</staff>
+      </direction>
+      <note default-x="15">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>5</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-60">down</stem>
+        <staff>1</staff>
+        <beam number="1">begin</beam>
+      </note>
+      <note default-x="38">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-61.5">down</stem>
+        <staff>1</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="61">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-63">down</stem>
+        <staff>1</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <direction>
+        <direction-type>
+          <wedge type="stop"/>
+        </direction-type>
+        <offset>1</offset>
+        <staff>1</staff>
+      </direction>
+      <note default-x="84">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-65">down</stem>
+        <staff>1</staff>
+        <beam number="1">end</beam>
+      </note>
+      <attributes>
+        <clef number="1">
+          <sign>F</sign>
+          <line>4</line>
+        </clef>
+      </attributes>
+      <note default-x="136">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-35">down</stem>
+        <staff>1</staff>
+        <beam number="1">begin</beam>
+      </note>
+      <note default-x="160">
+        <pitch>
+          <step>D</step>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-36.5">down</stem>
+        <staff>1</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="183">
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-38">down</stem>
+        <staff>1</staff>
+        <beam number="1">continue</beam>
+      </note>
+      <note default-x="206">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-40">down</stem>
+        <staff>1</staff>
+        <beam number="1">end</beam>
+        <notations>
+          <slur number="1" type="stop"/>
+        </notations>
+      </note>
+      <backup>
+        <duration>32</duration>
+      </backup>
+      <note default-x="15">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>8</duration>
+        <voice>3</voice>
+        <type>quarter</type>
+        <stem default-y="-50.5">down</stem>
+        <staff>2</staff>
+        <notations>
+          <slur bezier-x="-32" bezier-y="118" default-x="7" default-y="-5" number="2" type="stop"/>
+        </notations>
+      </note>
+      <note default-x="61">
+        <rest/>
+        <duration>8</duration>
+        <voice>3</voice>
+        <type>quarter</type>
+        <staff>2</staff>
+      </note>
+      <note default-x="134">
+        <rest/>
+        <duration>16</duration>
+        <voice>3</voice>
+        <type>half</type>
+        <staff>2</staff>
+      </note>
+    </measure>
+  </part>
+  <!--=========================================================-->
+</score-partwise>

+ 5929 - 0
demo/BrookeWestSample.musicxml

@@ -0,0 +1,5929 @@
+<?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">
+  <movement-title>West Point</movement-title>
+  <identification>
+    <creator type="composer">Jonatha Brooke</creator>
+    <rights>© 1995 Dog Dream Music. All rights reserved.</rights>
+    <encoding>
+      <software>Finale v25 for Mac</software>
+      <encoding-date>2017-12-12</encoding-date>
+      <supports attribute="new-system" element="print" type="yes" value="yes"/>
+      <supports attribute="new-page" element="print" type="yes" value="yes"/>
+      <supports element="accidental" type="yes"/>
+      <supports element="beam" type="yes"/>
+      <supports element="stem" type="yes"/>
+    </encoding>
+  </identification>
+  <defaults>
+    <scaling>
+      <millimeters>7.2319</millimeters>
+      <tenths>40</tenths>
+    </scaling>
+    <page-layout>
+      <page-height>1545</page-height>
+      <page-width>1194</page-width>
+      <page-margins type="both">
+        <left-margin>140</left-margin>
+        <right-margin>70</right-margin>
+        <top-margin>70</top-margin>
+        <bottom-margin>70</bottom-margin>
+      </page-margins>
+    </page-layout>
+    <system-layout>
+      <system-margins>
+        <left-margin>0</left-margin>
+        <right-margin>0</right-margin>
+      </system-margins>
+      <system-distance>181</system-distance>
+      <top-system-distance>70</top-system-distance>
+    </system-layout>
+    <staff-layout>
+      <staff-distance>81</staff-distance>
+    </staff-layout>
+    <appearance>
+      <line-width type="stem">0.957</line-width>
+      <line-width type="beam">5</line-width>
+      <line-width type="staff">1.4583</line-width>
+      <line-width type="light barline">1.4583</line-width>
+      <line-width type="heavy barline">5</line-width>
+      <line-width type="leger">1.875</line-width>
+      <line-width type="ending">1.4583</line-width>
+      <line-width type="wedge">0.9375</line-width>
+      <line-width type="enclosure">1.4583</line-width>
+      <line-width type="tuplet bracket">1.4583</line-width>
+      <note-size type="grace">50</note-size>
+      <note-size type="cue">50</note-size>
+      <distance type="hyphen">60</distance>
+      <distance type="beam">8</distance>
+    </appearance>
+    <music-font font-family="Jazz,handwritten" font-size="20.5"/>
+    <word-font font-family="JazzText" font-size="10.25"/>
+    <lyric-font font-family="JazzText" font-size="10.25"/>
+  </defaults>
+  <credit page="1">
+    <credit-type>composer</credit-type>
+    <credit-words default-x="1124" default-y="1393" font-size="12" font-weight="bold" halign="right" justify="center" valign="top" xml:space="preserve">Words and Music by
+Jonatha Brooke</credit-words>
+  </credit>
+  <credit page="1">
+    <credit-type>rights</credit-type>
+    <credit-words default-x="632" default-y="44" font-size="10" justify="center" valign="bottom">© 1995 Dog Dream Music. All rights reserved. Transcription by Ken Temple, Marian Russell, and Michael Good.</credit-words>
+  </credit>
+  <credit page="1">
+    <credit-type>title</credit-type>
+    <credit-words default-x="632" default-y="1458" font-size="24" font-weight="bold" justify="center" valign="top">West Point</credit-words>
+  </credit>
+  <part-list>
+    <score-part id="P1">
+      <part-name print-object="no">Voice</part-name>
+      <score-instrument id="P1-I3">
+        <instrument-name>Voice</instrument-name>
+        <instrument-sound>voice.vocals</instrument-sound>
+        <solo/>
+      </score-instrument>
+      <midi-instrument id="P1-I3">
+        <midi-channel>1</midi-channel>
+        <midi-program>55</midi-program>
+        <volume>80</volume>
+        <pan>0</pan>
+      </midi-instrument>
+    </score-part>
+    <score-part id="P2">
+      <part-name print-object="no">Guitar</part-name>
+      <score-instrument id="P2-I2">
+        <instrument-name>Acoustic Guitar (steel)</instrument-name>
+        <instrument-sound>pluck.guitar.steel-string</instrument-sound>
+      </score-instrument>
+      <midi-instrument id="P2-I2">
+        <midi-channel>2</midi-channel>
+        <midi-program>26</midi-program>
+        <volume>80</volume>
+        <pan>0</pan>
+      </midi-instrument>
+    </score-part>
+  </part-list>
+  <!--=========================================================-->
+  <part id="P1">
+    <measure number="1">
+      <print>
+        <measure-numbering>none</measure-numbering>
+      </print>
+      <attributes>
+        <divisions>4</divisions>
+        <key>
+          <fifths>6</fifths>
+          <mode>major</mode>
+        </key>
+        <time symbol="common">
+          <beats>4</beats>
+          <beat-type>4</beat-type>
+        </time>
+        <clef>
+          <sign>G</sign>
+          <line>2</line>
+        </clef>
+        <staff-details print-object="no"/>
+      </attributes>
+      <sound tempo="84"/>
+      <note>
+        <rest measure="yes"/>
+        <duration>16</duration>
+        <voice>1</voice>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="2">
+      <note>
+        <rest measure="yes"/>
+        <duration>16</duration>
+        <voice>1</voice>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="3">
+      <note>
+        <rest measure="yes"/>
+        <duration>16</duration>
+        <voice>1</voice>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="4">
+      <note>
+        <rest measure="yes"/>
+        <duration>16</duration>
+        <voice>1</voice>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="5">
+      <print new-system="yes"/>
+      <note>
+        <rest measure="yes"/>
+        <duration>16</duration>
+        <voice>1</voice>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="6">
+      <note>
+        <rest measure="yes"/>
+        <duration>16</duration>
+        <voice>1</voice>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="7">
+      <note>
+        <rest measure="yes"/>
+        <duration>16</duration>
+        <voice>1</voice>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="8">
+      <note>
+        <rest measure="yes"/>
+        <duration>16</duration>
+        <voice>1</voice>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="9">
+      <note>
+        <rest measure="yes"/>
+        <duration>16</duration>
+        <voice>1</voice>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="10" width="313">
+      <print new-system="yes">
+        <system-layout>
+          <system-distance>83</system-distance>
+        </system-layout>
+      </print>
+      <attributes>
+        <staff-details print-object="yes"/>
+      </attributes>
+      <note default-x="140">
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        <type>half</type>
+      </note>
+      <note default-x="216">
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+      </note>
+      <note default-x="257">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="5">up</stem>
+        <beam number="1">begin</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>I'm</text>
+        </lyric>
+      </note>
+      <note default-x="284">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="10">up</stem>
+        <beam number="1">end</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>begin</syllabic>
+          <text>re</text>
+        </lyric>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="11" width="357">
+      <note default-x="16">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="5">up</stem>
+        <beam number="1">begin</beam>
+        <beam number="2">forward hook</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>middle</syllabic>
+          <text>tra</text>
+        </lyric>
+      </note>
+      <note default-x="52">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="5">up</stem>
+        <beam number="1">continue</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>middle</syllabic>
+          <text>vel</text>
+        </lyric>
+      </note>
+      <note default-x="93">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="5">up</stem>
+        <beam number="1">continue</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>end</syllabic>
+          <text>ling</text>
+        </lyric>
+      </note>
+      <note default-x="134">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="5">up</stem>
+        <beam number="1">continue</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>this</text>
+        </lyric>
+      </note>
+      <note default-x="172">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <tie type="start"/>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="5">up</stem>
+        <beam number="1">end</beam>
+        <beam number="2">backward hook</beam>
+        <notations>
+          <tied type="start"/>
+        </notations>
+        <lyric default-y="-97" number="1">
+          <syllabic>begin</syllabic>
+          <text>life</text>
+        </lyric>
+      </note>
+      <note default-x="219">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <tie type="stop"/>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="5">up</stem>
+        <beam number="1">begin</beam>
+        <beam number="2">forward hook</beam>
+        <notations>
+          <tied type="stop"/>
+        </notations>
+      </note>
+      <note default-x="246">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem default-y="2.5">up</stem>
+        <beam number="1">end</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>end</syllabic>
+          <text>line</text>
+        </lyric>
+      </note>
+      <note default-x="293">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="5">up</stem>
+        <beam number="1">begin</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>that's</text>
+        </lyric>
+      </note>
+      <note default-x="331">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="10">up</stem>
+        <beam number="1">end</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>so</text>
+        </lyric>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="12" width="313">
+      <note default-x="25">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem default-y="5">up</stem>
+        <beam number="1">begin</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>close</text>
+        </lyric>
+      </note>
+      <note default-x="63">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <tie type="start"/>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="2.5">up</stem>
+        <beam number="1">end</beam>
+        <beam number="2">backward hook</beam>
+        <notations>
+          <tied type="start"/>
+        </notations>
+        <lyric default-y="-97" justify="left" number="1">
+          <syllabic>single</syllabic>
+          <text>to</text>
+          <extend/>
+        </lyric>
+      </note>
+      <note default-x="102">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <tie type="stop"/>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-5">up</stem>
+        <beam number="1">begin</beam>
+        <notations>
+          <tied type="stop"/>
+        </notations>
+      </note>
+      <note default-x="139">
+        <pitch>
+          <step>E</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <tie type="start"/>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-7">up</stem>
+        <beam number="1">end</beam>
+        <notations>
+          <tied type="start"/>
+        </notations>
+        <lyric default-y="-97" justify="left" number="1">
+          <syllabic>single</syllabic>
+          <text>home</text>
+          <extend/>
+        </lyric>
+      </note>
+      <note default-x="187">
+        <pitch>
+          <step>E</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <tie type="stop"/>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="-4">up</stem>
+        <notations>
+          <tied type="stop"/>
+        </notations>
+      </note>
+      <note default-x="228">
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+      </note>
+      <note default-x="251">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="-10">up</stem>
+        <beam number="1">begin</beam>
+        <beam number="2">begin</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>We</text>
+        </lyric>
+      </note>
+      <note default-x="283">
+        <pitch>
+          <step>E</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="-7">up</stem>
+        <beam number="1">end</beam>
+        <beam number="2">end</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>are</text>
+        </lyric>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="13" width="570">
+      <print new-system="yes">
+        <system-layout>
+          <system-distance>83</system-distance>
+        </system-layout>
+      </print>
+      <note default-x="142">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="0">up</stem>
+        <beam number="1">begin</beam>
+        <beam number="2">forward hook</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>on</text>
+        </lyric>
+      </note>
+      <note default-x="176">
+        <pitch>
+          <step>E</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="0">up</stem>
+        <beam number="1">continue</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>our</text>
+        </lyric>
+      </note>
+      <note default-x="219">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="0">up</stem>
+        <beam number="1">continue</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>way</text>
+        </lyric>
+      </note>
+      <note default-x="260">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="0">up</stem>
+        <beam number="1">continue</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>to</text>
+        </lyric>
+      </note>
+      <note default-x="303">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <tie type="start"/>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="0">up</stem>
+        <beam number="1">end</beam>
+        <beam number="2">backward hook</beam>
+        <notations>
+          <tied type="start"/>
+        </notations>
+        <lyric default-y="-97" justify="left" number="1">
+          <syllabic>single</syllabic>
+          <text>West</text>
+          <extend/>
+        </lyric>
+      </note>
+      <note default-x="357">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <tie type="stop"/>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="-15">up</stem>
+        <beam number="1">begin</beam>
+        <beam number="2">forward hook</beam>
+        <notations>
+          <tied type="stop"/>
+        </notations>
+      </note>
+      <note default-x="393">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem default-y="-17">up</stem>
+        <beam number="1">end</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>Point</text>
+        </lyric>
+      </note>
+      <note default-x="440">
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+      </note>
+      <note default-x="483">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="-10">up</stem>
+        <beam number="1">begin</beam>
+        <beam number="2">begin</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>Where</text>
+        </lyric>
+      </note>
+      <note default-x="533">
+        <pitch>
+          <step>E</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="-7.5">up</stem>
+        <beam number="1">end</beam>
+        <beam number="2">end</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>your</text>
+        </lyric>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="14" width="413">
+      <note default-x="15">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="-5">up</stem>
+        <beam number="1">begin</beam>
+        <beam number="2">begin</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>begin</syllabic>
+          <text>per</text>
+        </lyric>
+      </note>
+      <note default-x="57">
+        <pitch>
+          <step>E</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="-7.5">up</stem>
+        <beam number="1">end</beam>
+        <beam number="2">end</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>end</syllabic>
+          <text>fect,</text>
+        </lyric>
+      </note>
+      <note default-x="93">
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+      </note>
+      <note default-x="133">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem default-y="-9.5">up</stem>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>blond</text>
+        </lyric>
+      </note>
+      <note default-x="195">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="-20">up</stem>
+        <beam number="1">begin</beam>
+        <beam number="2">forward hook</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>begin</syllabic>
+          <text>cou</text>
+        </lyric>
+      </note>
+      <note default-x="233">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-20">up</stem>
+        <beam number="1">continue</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>end</syllabic>
+          <text>sin</text>
+        </lyric>
+      </note>
+      <note default-x="278">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-20">up</stem>
+        <beam number="1">continue</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>will</text>
+        </lyric>
+      </note>
+      <note default-x="333">
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-20">up</stem>
+        <beam number="1">continue</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>throw</text>
+        </lyric>
+      </note>
+      <note default-x="381">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="-20">up</stem>
+        <beam number="1">end</beam>
+        <beam number="2">backward hook</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>his</text>
+        </lyric>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="15" width="408">
+      <print new-system="yes">
+        <system-layout>
+          <system-distance>77</system-distance>
+        </system-layout>
+      </print>
+      <note default-x="146">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-19">up</stem>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>hat</text>
+        </lyric>
+      </note>
+      <note default-x="177">
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+      </note>
+      <note default-x="209">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="5">up</stem>
+        <beam number="1">begin</beam>
+        <beam number="2">begin</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>in</text>
+        </lyric>
+      </note>
+      <note default-x="238">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="7.5">up</stem>
+        <beam number="1">continue</beam>
+        <beam number="2">end</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>the</text>
+        </lyric>
+      </note>
+      <note default-x="268">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <tie type="start"/>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="10">up</stem>
+        <beam number="1">end</beam>
+        <notations>
+          <tied type="start"/>
+        </notations>
+        <lyric default-y="-97" justify="left" number="1">
+          <syllabic>single</syllabic>
+          <text>air</text>
+          <extend/>
+        </lyric>
+      </note>
+      <note default-x="310">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>8</duration>
+        <tie type="stop"/>
+        <tie type="start"/>
+        <voice>1</voice>
+        <type>half</type>
+        <stem default-y="10.5">up</stem>
+        <notations>
+          <tied type="stop"/>
+          <tied type="start"/>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="16" width="328">
+      <note default-x="10">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>8</duration>
+        <tie type="stop"/>
+        <voice>1</voice>
+        <type>half</type>
+        <stem default-y="10.5">up</stem>
+        <notations>
+          <tied type="stop"/>
+        </notations>
+      </note>
+      <note default-x="104">
+        <rest/>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+      </note>
+      <note default-x="151">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="-19">up</stem>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>And</text>
+        </lyric>
+      </note>
+      <note default-x="185">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="-20">up</stem>
+        <beam number="1">begin</beam>
+        <beam number="2">forward hook</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>we</text>
+        </lyric>
+      </note>
+      <note default-x="223">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-20">up</stem>
+        <beam number="1">continue</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>will</text>
+        </lyric>
+      </note>
+      <note default-x="273">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <tie type="start"/>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="-20">up</stem>
+        <beam number="1">end</beam>
+        <beam number="2">backward hook</beam>
+        <notations>
+          <tied type="start"/>
+        </notations>
+        <lyric default-y="-97" justify="left" number="1">
+          <syllabic>single</syllabic>
+          <text>watch</text>
+          <extend/>
+        </lyric>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="17" width="247">
+      <note default-x="13">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <tie type="stop"/>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="-15">up</stem>
+        <beam number="1">begin</beam>
+        <beam number="2">forward hook</beam>
+        <notations>
+          <tied type="stop"/>
+        </notations>
+      </note>
+      <note default-x="37">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem default-y="-16">up</stem>
+        <beam number="1">continue</beam>
+        <lyric default-y="-97" number="1">
+          <syllabic>single</syllabic>
+          <text>it</text>
+        </lyric>
+      </note>
+      <note default-x="70">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <tie type="start"/>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem default-y="-17.5">up</stem>
+        <beam number="1">end</beam>
+        <beam number="2">backward hook</beam>
+        <notations>
+          <tied type="start"/>
+        </notations>
+        <lyric default-y="-97" justify="left" number="1">
+          <syllabic>single</syllabic>
+          <text>fall</text>
+          <extend/>
+        </lyric>
+      </note>
+      <note default-x="120">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>12</duration>
+        <tie type="stop"/>
+        <voice>1</voice>
+        <type>half</type>
+        <dot/>
+        <stem default-y="-19">up</stem>
+        <notations>
+          <tied type="stop"/>
+        </notations>
+      </note>
+    </measure>
+  </part>
+  <!--=========================================================-->
+  <part id="P2">
+    <measure number="1" width="268">
+      <print>
+        <system-layout>
+          <top-system-distance>211</top-system-distance>
+        </system-layout>
+        <measure-numbering>none</measure-numbering>
+      </print>
+      <attributes>
+        <divisions>4</divisions>
+        <key print-object="no">
+          <fifths>6</fifths>
+          <mode>major</mode>
+        </key>
+        <clef>
+          <sign>TAB</sign>
+          <line>5</line>
+        </clef>
+        <staff-details>
+          <staff-lines>6</staff-lines>
+          <staff-tuning line="1">
+            <tuning-step>D</tuning-step>
+            <tuning-octave>2</tuning-octave>
+          </staff-tuning>
+          <staff-tuning line="2">
+            <tuning-step>A</tuning-step>
+            <tuning-octave>2</tuning-octave>
+          </staff-tuning>
+          <staff-tuning line="3">
+            <tuning-step>D</tuning-step>
+            <tuning-octave>3</tuning-octave>
+          </staff-tuning>
+          <staff-tuning line="4">
+            <tuning-step>G</tuning-step>
+            <tuning-octave>3</tuning-octave>
+          </staff-tuning>
+          <staff-tuning line="5">
+            <tuning-step>B</tuning-step>
+            <tuning-octave>3</tuning-octave>
+          </staff-tuning>
+          <staff-tuning line="6">
+            <tuning-step>D</tuning-step>
+            <tuning-octave>4</tuning-octave>
+          </staff-tuning>
+          <capo>4</capo>
+          <staff-size>183</staff-size>
+        </staff-details>
+      </attributes>
+      <sound tempo="84"/>
+      <harmony default-y="40" font-size="15.4">
+        <root>
+          <root-step>B</root-step>
+        </root>
+        <kind halign="left" text="Maj7">major-seventh</kind>
+        <bass>
+          <bass-step>D</bass-step>
+          <bass-alter>1</bass-alter>
+        </bass>
+      </harmony>
+      <direction placement="above">
+        <direction-type>
+          <words default-y="72" relative-x="-22">Tuning D-A-D-G-B-D, Capo 4th fret</words>
+        </direction-type>
+      </direction>
+      <note default-x="41">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="58">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="86">
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="114">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="131" print-dot="no">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="168">
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="196">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="213">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="230" print-dot="no">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="2" width="214">
+      <note default-x="11">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="26">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="52">
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="76">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="91" print-dot="no">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="125">
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="149">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="165">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="181" print-dot="no">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="3" width="218">
+      <harmony default-y="40" font-size="15.4">
+        <root>
+          <root-step>B</root-step>
+        </root>
+        <kind halign="left" text="Maj7">major-seventh</kind>
+      </harmony>
+      <note default-x="12" print-dot="no">
+        <pitch>
+          <step>B</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="12" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="11" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="11" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="50" print-dot="no">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>3</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="49" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="88" print-dot="no">
+        <pitch>
+          <step>B</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="88" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="86" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="87" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="125" print-dot="no">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>3</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="125" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="163">
+        <pitch>
+          <step>B</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="163">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="162">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="162">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="180" print-dot="no">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="4" width="284">
+      <note default-x="12" print-dot="no">
+        <pitch>
+          <step>B</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="12" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="11" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="11" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="58" print-dot="no">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>3</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="58" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="105">
+        <pitch>
+          <step>B</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="105">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="103">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="125">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="161">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="196">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="216">
+        <pitch>
+          <step>E</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <pull-off number="1" type="start">P</pull-off>
+            <string>5</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="237" print-dot="no">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <pull-off number="1" type="stop"/>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="5" width="206">
+      <print new-system="yes">
+        <system-layout>
+          <system-distance>83</system-distance>
+        </system-layout>
+      </print>
+      <harmony default-y="40" font-size="15.4">
+        <root>
+          <root-step>G</root-step>
+          <root-alter>1</root-alter>
+        </root>
+        <kind halign="left" text="m9">minor-ninth</kind>
+      </harmony>
+      <note default-x="11">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="26">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="51" print-dot="no">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="51" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="51" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="51" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="83" print-dot="no">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="83" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="84" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="84" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="117" print-dot="no">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="117" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="117" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="117" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="149">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="149">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="150">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="150">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="165">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="165">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="165">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="165">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="189">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="6" width="206">
+      <note default-x="12">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="27">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="51" print-dot="no">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="51" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="51" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="84" print-dot="no">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="84" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="84" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="84" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="117" print-dot="no">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="117" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="117" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="117" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="150">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="150">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="150">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="150">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="165">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="166">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="166">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="166">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="190">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="7" width="190">
+      <harmony default-y="40" font-size="15.4">
+        <root>
+          <root-step>F</root-step>
+          <root-alter>1</root-alter>
+        </root>
+        <kind halign="left" text="">major</kind>
+      </harmony>
+      <note default-x="12" print-dot="no">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="12" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="44" print-dot="no">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <harmony default-y="40" font-size="15.4">
+        <root>
+          <root-step>C</root-step>
+          <root-alter>1</root-alter>
+        </root>
+        <kind halign="left" text="7sus4">suspended-fourth</kind>
+        <degree print-object="no">
+          <degree-value>7</degree-value>
+          <degree-alter>0</degree-alter>
+          <degree-type>add</degree-type>
+        </degree>
+      </harmony>
+      <note default-x="77" print-dot="no">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="76" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="77" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="109" print-dot="no">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="110" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="110" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>3</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="110" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="142">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="142">
+        <chord/>
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="142">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="157" print-dot="no">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="8" width="190">
+      <harmony default-y="40" font-size="15.4">
+        <root>
+          <root-step>F</root-step>
+          <root-alter>1</root-alter>
+        </root>
+        <kind halign="left" text="">major</kind>
+      </harmony>
+      <note default-x="11" print-dot="no">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="11" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="44" print-dot="no">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <harmony default-y="40" font-size="15.4">
+        <root>
+          <root-step>C</root-step>
+          <root-alter>1</root-alter>
+        </root>
+        <kind halign="left" text="7sus4">suspended-fourth</kind>
+        <degree print-object="no">
+          <degree-value>7</degree-value>
+          <degree-alter>0</degree-alter>
+          <degree-type>add</degree-type>
+        </degree>
+      </harmony>
+      <note default-x="77" print-dot="no">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="76" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="77" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="109" print-dot="no">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="110" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="110" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>3</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="110" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="142">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="142">
+        <chord/>
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="142">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="157" print-dot="no">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="9" width="191">
+      <harmony default-y="40" font-size="15.4">
+        <root>
+          <root-step>F</root-step>
+          <root-alter>1</root-alter>
+        </root>
+        <kind halign="left" text="">major</kind>
+      </harmony>
+      <note default-x="11" print-dot="no">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="11" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="44" print-dot="no">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <harmony default-y="40" font-size="15.4">
+        <root>
+          <root-step>C</root-step>
+          <root-alter>1</root-alter>
+        </root>
+        <kind halign="left" text="7sus4">suspended-fourth</kind>
+        <degree print-object="no">
+          <degree-value>7</degree-value>
+          <degree-alter>0</degree-alter>
+          <degree-type>add</degree-type>
+        </degree>
+      </harmony>
+      <note default-x="77" print-dot="no">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="77" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="77" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="109" print-dot="no">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="110" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="110" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>3</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="110" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="143">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="142">
+        <chord/>
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="143">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="157" print-dot="no">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="10" width="313">
+      <print new-system="yes"/>
+      <note default-x="143" print-dot="no">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="143" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="170" print-dot="no">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="198" print-dot="no">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="198" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="198" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="230" print-dot="no">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="230" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="231" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>3</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="230" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="258">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="258">
+        <chord/>
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="258">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="272" print-dot="no">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="11" width="357">
+      <note default-x="17">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="53">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="94">
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="135">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="154" print-dot="no">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="247">
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="278">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="293">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="313" print-dot="no">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="12" width="313">
+      <note default-x="26">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="39">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="64">
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="111">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="139" print-dot="no">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="195">
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="214">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="228">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="243" print-dot="no">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="13" width="570">
+      <print new-system="yes"/>
+      <note default-x="144" print-dot="no">
+        <pitch>
+          <step>B</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="144" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="143" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="143" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="220" print-dot="no">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>3</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="220" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="284" print-dot="no">
+        <pitch>
+          <step>B</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="284" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="282" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="283" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="395" print-dot="no">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>3</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="395" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="441">
+        <pitch>
+          <step>B</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="441">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="440">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="440">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="463" print-dot="no">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="14" width="413">
+      <note default-x="17" print-dot="no">
+        <pitch>
+          <step>B</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="17" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="16" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="16" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="115" print-dot="no">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>3</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="115" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="163">
+        <pitch>
+          <step>B</step>
+          <octave>2</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="163">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="162">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="181">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="235">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="280">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>5</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="306">
+        <pitch>
+          <step>E</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <pull-off number="1" type="start">P</pull-off>
+            <string>5</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="335" print-dot="no">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <pull-off number="1" type="stop"/>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="15" width="408">
+      <print new-system="yes"/>
+      <note default-x="147">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="162">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="190" print-dot="no">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="190" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="190" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="190" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="269" print-dot="no">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="269" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="269" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="269" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="319" print-dot="no">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="319" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="319" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="319" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="351">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="351">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="351">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="351">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="367">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="367">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="367">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="367">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="391">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="16" width="328">
+      <note default-x="11">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="26">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="50" print-dot="no">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="50" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="50" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="83" print-dot="no">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="83" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="83" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="83" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="120" print-dot="no">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="121" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="121" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="121" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="186">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="186">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="186">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="186">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="224">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="224">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="224">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="224">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="274">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+    </measure>
+    <!--=======================================================-->
+    <measure number="17" width="247">
+      <note default-x="14" print-dot="no">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>2</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>6</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="14" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="71" print-dot="no">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>4</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="138" print-dot="no">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="138" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="138" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="169" print-dot="no">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="169" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="170" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>2</string>
+            <fret>3</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="169" print-dot="no">
+        <chord/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>1</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="200">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>5</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="200">
+        <chord/>
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>2</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="200">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+        </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>3</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+      <note default-x="216" print-dot="no">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+        </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>none</stem>
+        <notations>
+          <technical>
+            <string>4</string>
+            <fret>0</fret>
+          </technical>
+        </notations>
+      </note>
+    </measure>
+  </part>
+  <!--=========================================================-->
+</score-partwise>

+ 117 - 0
demo/embedded_demo.html

@@ -0,0 +1,117 @@
+<!doctype html>
+<html lang="en">
+
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+
+    <title>OpenSheetMusicDisplay as an embedded iframe</title>
+    <meta name="description" content="A showcase for OpenSheetMusicDisplay.">
+    <meta name="author" content="OpenSheetMusicDisplay contributors">
+
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" type="text/javascript"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.0/semantic.min.js"
+        type="text/javascript"></script>
+    <link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.0/semantic.css" media="all" rel="stylesheet" />
+    <link rel="icon" href="./favicon.ico?" type="image/x-icon" />
+</head>
+
+<body>
+    <br>
+    <div style="text-align: center;width: 60%;margin:0 auto">
+        <h1 class="ui centered header" id="header">
+            <img src="./favicon.ico?" class="ui image">
+
+        </h1>
+        <h2>
+            OpenSheetMusicDisplay as an embedded iframe
+        </h2>
+    </div>
+    <div style="padding: 40px;margin:0 auto">
+        <h3>
+            Use parameters:
+        </h3>
+        <p>
+            <table class="ui table celled ">
+                <tr>
+                    <th>Parameter</th>
+                    <th>Values</th>
+                    <th>Default</th>
+                    <th>Description</th>
+                </tr>
+                <tr>
+                    <td><b>embedded</b></td>
+                    <td></td>
+                    <td></td>
+                    <td>Activates the embedded mode: controls are hidden (unless other parameters specified)
+                    </td>
+                </tr>
+                <tr>
+                    <td>showControls</td>
+                    <td>"0" and "1"</td>
+                    <td>"0" = OFF</td>
+                    <td>If active, the controls of OSMD are shown like usually.</td>
+                </tr>
+                <tr>
+                    <td>showZoomControl</td>
+                    <td>"0" and "1"</td>
+                    <td>"0" = OFF</td>
+                    <td>If showControls == 0 and showZoomControl == 1, a small zoomControl widget will be displayed to safe space
+                    </td>
+                </tr>
+                <tr>
+                    <td>showHeader</td>
+                    <td>"0" and "1"</td>
+                    <td>"0" = OFF</td>
+                    <td>If activated, the OSMD title will be rendered on top of the sheet</td>
+                </tr>
+                <tr>
+                    <td>zoom</td>
+                    <td>floating number between 0.1 and 5.0</td>
+                    <td>1.0</td>
+                    <td>If given, the OSMD zoom level will be set at loading time</td>
+                </tr>
+                <tr>
+                    <td>overflow</td>
+                    <td>"hidden" , "auto", "scroll", "visible"</td>
+                    <td>"auto"</td>
+                    <td>If given, this attribute will refresh the state of the css attribute overflow to control the
+                        scrollbar.
+                    </td>
+                </tr>
+                <tr>
+                    <td>openUrl</td>
+                    <td>valid URL pointing to a valid musicxml file</td>
+                    <td>"auto"</td>
+                    <td>If given, this attribute open the given musicxml file and display it.
+                        <p>
+                            <h5>Important:</h5>
+                            For now, the resources must be hosted on the same server, otherwise it will be blocked by
+                            <a href="https://de.wikipedia.org/wiki/Cross-Origin_Resource_Sharing">CORS policy</a>.
+                        </p>
+                        <p>
+                            <h5>Examples:</h5>
+                            <a
+                                href="http://localhost:8000/?embedded&openUrl=http://localhost:8000/BrookeWestSample.musicxml">
+                                http://localhost:8000/?embedded&openUrl=http://localhost:8000/BrookeWestSample.musicxml</a>
+                                <br>
+                            <a
+                                href="http://localhost:8000/?embedded&openUrl=https://wpmedia.musicxml.com/wp-content/uploads/2017/12/MahlFaGe4Sample.mxl">http://localhost:8000/?embedded&openUrl=https://wpmedia.musicxml.com/wp-content/uploads/2017/12/MahlFaGe4Sample.mxl</a>
+
+                        </p>
+                    </td>
+                </tr>
+            </table>
+    </div>
+    <div>
+        <center>
+            <h4>Example: http://localhost:8000/?embedded&showZoomControl=1&zoom=0.5</h4>
+            <iframe src="http://localhost:8000/?embedded&showZoomControl=1&zoom=0.5" width="800" height="1500"
+                frameborder="1"></iframe>
+        </center>
+    </div>
+
+
+</body>
+
+</html>

+ 80 - 20
demo/index.html

@@ -3,30 +3,29 @@
 <head>
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
-    
+
     <title><%= htmlWebpackPlugin.options.title %></title>
     <meta name="description" content="A showcase for OpenSheetMusicDisplay.">
     <meta name="author" content="OpenSheetMusicDisplay contributors">
-    <!-- Demo js file is included automatically by webpack druing build -->
+    <!-- Demo index.js file is included automatically by webpack during build -->
     <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" type="text/javascript"></script>    
     <script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.0/semantic.min.js" type="text/javascript"></script>    
     <link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.0/semantic.css" media="all" rel="stylesheet"/>
     <link rel="icon" href="./favicon.ico?" type="image/x-icon"/>
 </head>
 <body>
-<br>
-<h1 class="ui centered header">
+<h1 class="ui centered header" id="header" style="opacity: 0.0">
     <img src="./favicon.ico?" class="ui image">
     <%= htmlWebpackPlugin.options.title %>
 </h1>
-<div class="ui four column grid container">
+<div class="ui four column grid container" id="divControls" style="visibility: hidden; opacity: 0.0">
     <div class="column">
         <h3 class="ui header">Select a sample:</h3>
-        <select class="ui selection dropdown" id="selectSample" style="width:320px; height:38%"></select>
+        <select class="ui selection dropdown" id="selectSample" style="width:320px; height:38%; visibility: hidden"></select>
     </div>
-    <div class="column">
+    <div class="column" id="backend-select-div" style="visibility: hidden">
         <h3 class="ui header">Render backend:</h3>
-        <select class="ui selection dropdown" id="backend-select" value="svg" style="height:38%">
+        <select class="ui selection dropdown" id="backend-select" value="svg" style="height:38%; visibility: hidden;">
             <option value="svg">SVG</option>
             <option value="canvas">Canvas</option>>
         </select>
@@ -62,11 +61,17 @@
                     </div>
                 </div>
             </div>
+            <div class="item">
+                <div class="ui toggle checkbox">
+                    <input type="checkbox" name="public" id="follow-cursor-checkbox">
+                    <label>Follow Cursor</label>
+                </div>
+            </div>
         </div>
-    </div>         
-    <div class="column">
+    </div>
+    <div class="column" id="zoomControls">
         <h3 class="ui header">Zoom controls:</h3>
-        <div class="ui buttons">
+        <div class="ui buttons" id="zoomControlsButtons">
             <div class="ui button" id="zoom-in-btn">
                 <i class="search plus icon"></i>
             </div>
@@ -78,7 +83,7 @@
     </div>
     <div class="column">
         <h3 class="ui header">Show bounding box for:</h3>
-        <select class="ui selection dropdown" id="selectBounding">
+        <select class="ui selection dropdown" id="selectBounding" style="visibility: hidden;">
             <option value="none">None</option>
             <option value="all">All</option>
             <option value="VexFlowMeasure">Measures</option>
@@ -114,19 +119,74 @@
         <div>
             <div class="ui vertical buttons">
                 <div class="ui button" id="debug-re-render-btn">Re-render</div>
-                
+
             </div>
             <div class="ui vertical buttons">
-                <div class="ui button" id="debug-clear-btn"">Clear</div>
+                <div class="ui button" id="debug-clear-btn">Clear</div>
+            </div>
+        </div>
+    </div>
+    <div class="column">
+        <h3 class="ui header">Page size:</h3>
+        <select class="ui selection dropdown" id="selectPageSize"  style="visibility: hidden;">
+            <option value="endless">endless</option>
+            <option value="A3 P">A3 Portrait</option>
+            <option value="A3 L">A3 Landscape</option>
+            <option value="A4 P">A4 Portrait</option>
+            <option value="A4 L">A4 Landscape</option>
+            <option value="A5 P">A5 Portrait</option>
+            <option value="A5 L">A5 Landscape</option>
+            <option value="Letter P">Letter Portrait</option>
+            <option value="Letter L">Letter Landscape</option>
+        </select>
+        <div class="ui button" id="print-pdf-btn">Print to Pdf</div>
+    </div>
+</div>
+<div id="optionalControls" style="opacity: 0.0; width: 95%; display: block">
+    <div class="ui three column grid container" style="padding: 10px; margin-right: auto; margin-left: auto" id="optionalControlsColumnContainer">
+        <div class="column" id="zoomControlsButtons-optional-column" style="min-width: 30%; opacity: 0.0">
+            <div class="ui buttons" id="zoomControlsButtons-optional">
+                <div class="ui button" id="zoom-in-btn-optional">
+                    <i class="search plus icon"></i>
+                </div>
+                <div class="ui button" id="zoom-out-btn-optional">
+                    <i class="search minus icon"></i>
+                </div>
+            </div>
+            <h4 id="zoom-str-optional">???</h4>
+        </div>
+        <div class="column" id="print-pdf-btn-optional-column" style="opacity: 0.0; max-width: 25%;">
+            <div class="ui button" id="print-pdf-btn-optional">Print to Pdf</div>
+        </div>
+        <div class="column" id="selectPageSize-optional-column" style="opacity: 0.0; min-width: 35%">
+            <div class="ui two column grid container">
+            <div class="column" style="margin-top: 8px">
+            <h3>Format:</h3>
+            </div>
+            <div class="column">
+            <select class="ui selection dropdown" id="selectPageSize-optional">
+                <option value="endless">endless</option>
+                <option value="A3 P">A3 Portrait</option>
+                <option value="A3 L">A3 Landscape</option>
+                <option value="A4 P">A4 Portrait</option>
+                <option value="A4 L">A4 Landscape</option>
+                <option value="A5 P">A5 Portrait</option>
+                <option value="A5 L">A5 Landscape</option>
+                <option value="Letter P">Letter Portrait</option>
+                <option value="Letter L">Letter Landscape</option>
+            </select>
+            </div>
             </div>
         </div>
     </div>
 </div>
-<table cellspacing="0" style="max-width:700px;">
-    <tr id="error-tr">
-        <td></td>
-        <td id="error-td"></td>
-    </tr>
-</table>
+<div>
+    <table cellspacing="0" style="max-width:700px;">
+        <tr id="error-tr">
+            <td></td>
+            <td id="error-td"></td>
+        </tr>
+    </table>
+</div>
 </body>
 </html>

+ 647 - 108
demo/index.js

@@ -1,93 +1,344 @@
 import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMusicDisplay';
+import { BackendType } from '../src/OpenSheetMusicDisplay/OSMDOptions';
+import * as jsPDF  from '../node_modules/jspdf-yworks/dist/jspdf.min';
+import * as svg2pdf from '../node_modules/svg2pdf.js/dist/svg2pdf.min';
+// import { Fraction } from '../src/Common/DataObjects/Fraction';
 
 /*jslint browser:true */
 (function () {
     "use strict";
     var openSheetMusicDisplay;
     var sampleFolder = process.env.STATIC_FILES_SUBFOLDER ? process.env.STATIC_FILES_SUBFOLDER + "/" : "",
-    samples = {
-        "Beethoven, L.v. - An die ferne Geliebte": "Beethoven_AnDieFerneGeliebte.xml",
-        "Clementi, M. - Sonatina Op.36 No.1 Pt.1": "MuzioClementi_SonatinaOpus36No1_Part1.xml",
-        "Clementi, M. - Sonatina Op.36 No.1 Pt.2": "MuzioClementi_SonatinaOpus36No1_Part2.xml",
-        "Clementi, M. - Sonatina Op.36 No.3 Pt.1": "MuzioClementi_SonatinaOpus36No3_Part1.xml",
-        "Clementi, M. - Sonatina Op.36 No.3 Pt.2": "MuzioClementi_SonatinaOpus36No3_Part2.xml",
-        "Bach, J.S. - Praeludium in C-Dur BWV846 1": "JohannSebastianBach_PraeludiumInCDur_BWV846_1.xml",
-        "Bach, J.S. - Air": "JohannSebastianBach_Air.xml",
-        "Gounod, C. - Méditation": "CharlesGounod_Meditation.xml",
-        "Haydn, J. - Concertante Cello": "JosephHaydn_ConcertanteCello.xml",
-        "Joplin, S. - Elite Syncopations": "ScottJoplin_EliteSyncopations.xml",
-        "Joplin, S. - The Entertainer": "ScottJoplin_The_Entertainer.xml",
-        "Mozart, W.A. - An Chloe": "Mozart_AnChloe.xml",
-        "Mozart, W.A. - Das Veilchen": "Mozart_DasVeilchen.xml",
-        "Mozart, W.A.- Clarinet Quintet (Excerpt)": "Mozart_Clarinet_Quintet_Excerpt.mxl",
-        "Mozart/Holzer - Land der Berge (national anthem of Austria)": "Land_der_Berge.musicxml",
-        "OSMD Function Test - All": "OSMD_function_test_all.xml",
-        "OSMD Function Test - Autobeam": "OSMD_function_test_autobeam.musicxml",
-        "OSMD Function Test - Accidentals": "OSMD_function_test_accidentals.musicxml",
-        "OSMD Function Test - Color": "OSMD_function_test_color.musicxml",
-        "OSMD Function Test - Drumset": "OSMD_function_test_drumset.musicxml",
-        "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 - Notehead Shapes": "OSMD_function_test_noteheadShapes.musicxml",
-        "OSMD Function Test - Ornaments": "OSMD_function_test_Ornaments.xml",
-        "Schubert, F. - An Die Musik": "Schubert_An_die_Musik.xml",
-        "Actor, L. - Prelude (Sample)": "ActorPreludeSample.xml",
-        "Anonymous - Saltarello": "Saltarello.mxl",
-        "Debussy, C. - Mandoline": "Debussy_Mandoline.xml",
-        "Levasseur, F. - Parlez Mois": "Parlez-moi.mxl",
-        "Schumann, R. - Dichterliebe": "Dichterliebe01.xml",
-        "Telemann, G.P. - Sonate-Nr.1.1-Dolce": "TelemannWV40.102_Sonate-Nr.1.1-Dolce.xml",
-        "Telemann, G.P. - Sonate-Nr.1.2-Allegro": "TelemannWV40.102_Sonate-Nr.1.2-Allegro-F-Dur.xml",
-    },
-
-    zoom = 1.0,
-    // HTML Elements in the page
-    err,
-    error_tr,
-    canvas,
-    selectSample,
-    selectBounding,
-    skylineDebug,
-    bottomlineDebug,
-    zoomIn,
-    zoomOut,
-    zoomDiv,
-    custom,
-    nextCursorBtn,
-    resetCursorBtn,
-    showCursorBtn,
-    hideCursorBtn,
-    backendSelect,
-    debugReRenderBtn,
-    debugClearBtn;
+        samples = {
+            "Beethoven, L.v. - An die ferne Geliebte": "Beethoven_AnDieFerneGeliebte.xml",
+            "Clementi, M. - Sonatina Op.36 No.1 Pt.1": "MuzioClementi_SonatinaOpus36No1_Part1.xml",
+            "Clementi, M. - Sonatina Op.36 No.1 Pt.2": "MuzioClementi_SonatinaOpus36No1_Part2.xml",
+            "Clementi, M. - Sonatina Op.36 No.3 Pt.1": "MuzioClementi_SonatinaOpus36No3_Part1.xml",
+            "Clementi, M. - Sonatina Op.36 No.3 Pt.2": "MuzioClementi_SonatinaOpus36No3_Part2.xml",
+            "Bach, J.S. - Praeludium in C-Dur BWV846 1": "JohannSebastianBach_PraeludiumInCDur_BWV846_1.xml",
+            "Bach, J.S. - Air": "JohannSebastianBach_Air.xml",
+            "Gounod, C. - Méditation": "CharlesGounod_Meditation.xml",
+            "Haydn, J. - Concertante Cello": "JosephHaydn_ConcertanteCello.xml",
+            "Joplin, S. - Elite Syncopations": "ScottJoplin_EliteSyncopations.xml",
+            "Joplin, S. - The Entertainer": "ScottJoplin_The_Entertainer.xml",
+            "Mozart, W.A. - An Chloe": "Mozart_AnChloe.xml",
+            "Mozart, W.A. - Das Veilchen": "Mozart_DasVeilchen.xml",
+            "Mozart, W.A. - Clarinet Quintet (Excerpt)": "Mozart_Clarinet_Quintet_Excerpt.mxl",
+            "Mozart, W.A. - String Quartet in G, K. 387, 1st Mvmt Excerpt": "Mozart_String_Quartet_in_G_K._387_1st_Mvmnt_excerpt.musicxml",
+            "Mozart/Holzer - Land der Berge (national anthem of Austria)": "Land_der_Berge.musicxml",
+            "OSMD Function Test - All": "OSMD_function_test_all.xml",
+            "OSMD Function Test - Accidentals": "OSMD_function_test_accidentals.musicxml",
+            "OSMD Function Test - Autobeam": "OSMD_function_test_autobeam.musicxml",
+            "OSMD Function Test - Auto-/Custom-Coloring": "OSMD_function_test_auto-custom-coloring-entchen.musicxml",
+            "OSMD Function Test - Bar lines": "OSMD_function_test_bar_lines.musicxml",
+            "OSMD Function Test - Chord Symbols": "OSMD_function_test_chord_symbols.musicxml",
+            "OSMD Function Test - Color (from XML)": "OSMD_function_test_color.musicxml",
+            "OSMD Function Test - Container height (compacttight mode)": "OSMD_Function_Test_Container_height.musicxml",
+            "OSMD Function Test - Drumset": "OSMD_function_test_drumset.musicxml",
+            "OSMD Function Test - Drums on one Line": "OSMD_Function_Test_Drums_one_line_snare_plus_piano.musicxml", 
+            "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 - Metronome Marks": "OSMD_function_test_metronome_marks.mxl",
+            "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",
+            "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 - Tabulature MultiBends": "OSMD_Function_Test_Tablature_Multibends.musicxml",
+            "OSMD Function Test - Tabulature All Effects": "OSMD_Function_Test_Tablature_Alleffects.musicxml",
+            "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",
+            "OSMD Function Test - Auto Multirest Measures Single Staff": "Test_Auto_Multirest_1.musicxml",
+            "OSMD Function Test - Auto Multirest Measures Multiple Staves": "Test_Auto_Multirest_2.musicxml",
+            "Schubert, F. - An Die Musik": "Schubert_An_die_Musik.xml",
+            "Actor, L. - Prelude (Large Sample, loading time)": "ActorPreludeSample.xml",
+            "Actor, L. - Prelude (Large, No Print Part Names)": "ActorPreludeSample_PartName.xml",
+            "Anonymous - Saltarello": "Saltarello.mxl",
+            "Debussy, C. - Mandoline": "Debussy_Mandoline.xml",
+            "Levasseur, F. - Parlez Mois": "Parlez-moi.mxl",
+            "Schumann, R. - Dichterliebe": "Dichterliebe01.xml",
+            "Telemann, G.P. - Sonate-Nr.1.1-Dolce": "TelemannWV40.102_Sonate-Nr.1.1-Dolce.xml",
+            "Telemann, G.P. - Sonate-Nr.1.2-Allegro": "TelemannWV40.102_Sonate-Nr.1.2-Allegro-F-Dur.xml",
+        },
+
+        zoom = 1.0,
+        // HTML Elements in the page
+        divControls,
+        zoomControls,
+        header,
+        err,
+        error_tr,
+        canvas,
+        selectSample,
+        selectBounding,
+        skylineDebug,
+        bottomlineDebug,
+        zoomIns,
+        zoomOuts,
+        zoomDivs,
+        custom,
+        nextCursorBtn,
+        resetCursorBtn,
+        followCursorCheckbox,
+        showCursorBtn,
+        hideCursorBtn,
+        backendSelect,
+        backendSelectDiv,
+        debugReRenderBtn,
+        debugClearBtn,
+        selectPageSizes,
+        printPdfBtns;
+    
+    // manage option setting and resetting for specific samples, e.g. in the autobeam sample autobeam is set to true, otherwise reset to previous state
+    // TODO design a more elegant option state saving & restoring system, though that requires saving the options state in OSMD
+    var minMeasureToDrawStashed = 1;
+    var maxMeasureToDrawStashed = Number.MAX_SAFE_INTEGER;
+    var measureToDrawRangeNeedsReset = false;
+    var drawingParametersStashed = "default";
+    var drawingParametersNeedsReset = false;
+    var autobeamOptionNeedsReset = false;
+    var autobeamOptionStashedValue = false;
+    var autoCustomColoringOptionNeedsReset = false;
+    var autoCustomColoringOptionStashedValue = false;
+    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;
+    var showPageFormatControl = false;
+    var showZoomControl = true;
+    var showHeader = true;
+    var showDebugControls = false;
+
+    if (process.env.OSMD_DEMO_TITLE) {
+        document.title = process.env.OSMD_DEMO_TITLE;
+    }
 
     // Initialization code
     function init() {
         var name, option;
 
+        // Handle window parameter
+        var paramEmbedded = findGetParameter('embedded');
+        var paramShowControls = findGetParameter('showControls');
+        var paramShowPageFormatControl = findGetParameter('showPageFormatControl');
+        var paramShowExportPdfControl = findGetParameter('showExportPdfControl');
+        var paramShowZoomControl = findGetParameter('showZoomControl');
+        var paramShowHeader = findGetParameter('showHeader');
+        var paramZoom = findGetParameter('zoom');
+        var paramOverflow = findGetParameter('overflow');
+        var paramOpenUrl = findGetParameter('openUrl');
+        var paramDebugControls = findGetParameter('debugControls');
+
+        var paramCompactMode = findGetParameter('compactMode');
+        var paramMeasureRangeStart = findGetParameter('measureRangeStart');
+        var paramMeasureRangeEnd = findGetParameter('measureRangeEnd');
+        var paramPageFormat = findGetParameter('pageFormat');
+        var paramPageBackgroundColor = findGetParameter('pageBackgroundColor');
+        var paramBackendType = findGetParameter('backendType');
+        var paramPageWidth = findGetParameter('pageWidth');
+        var paramPageHeight = findGetParameter('pageHeight');
+
+        var paramHorizontalScrolling = findGetParameter('horizontalScrolling');
+        var paramSingleHorizontalStaffline = findGetParameter('singleHorizontalStaffline');
+
+        showHeader = (paramShowHeader !== '0');
+        showControls = false;
+        if (paramEmbedded) {
+            showControls = paramShowControls !== '0';
+            showZoomControl = paramShowZoomControl !== '0';
+            showPageFormatControl = paramShowPageFormatControl !== '0';
+            showExportPdfControl = paramShowExportPdfControl !== '0';
+        }
+
+        if (paramZoom) {
+            if (paramZoom > 0.1 && paramZoom < 5.0) {
+                zoom = paramZoom;
+            }
+        }
+        if (paramOverflow && typeof paramOverflow === 'string') {
+            if (paramOverflow === 'hidden' || paramOverflow === 'auto' || paramOverflow === 'scroll' || paramOverflow === 'visible') {
+                document.body.style.overflow = paramOverflow;
+            }
+        }
+        
+        var compactMode = paramCompactMode && paramCompactMode !== '0';
+        var measureRangeStart = paramMeasureRangeStart ? Number.parseInt(paramMeasureRangeStart) : 0;
+        var measureRangeEnd = paramMeasureRangeEnd ? Number.parseInt(paramMeasureRangeEnd) : Number.MAX_SAFE_INTEGER;
+        if (measureRangeStart && measureRangeEnd && measureRangeEnd < measureRangeStart) {
+            console.log("[OSMD] warning: measure range end parameter should not be smaller than measure range start. We've set start measure = end measure now.")
+            measureRangeStart = measureRangeEnd;
+        }
+        let pageFormat = paramPageFormat ? paramPageFormat : "Endless";
+        if (paramPageHeight && paramPageWidth) {
+            pageFormat = `${paramPageWidth}x${paramPageHeight}`
+        }
+        var pageBackgroundColor = paramPageBackgroundColor ? "#" + paramPageBackgroundColor : undefined; // vexflow format, see OSMDOptions. can't use # in parameters.
+        //console.log("demo: osmd pagebgcolor: " + pageBackgroundColor);
+        var backendType = (paramBackendType && paramBackendType.toLowerCase) ? paramBackendType : "svg";
+
+        var horizontalScrolling = paramHorizontalScrolling === '1';
+        var singleHorizontalStaffline = paramSingleHorizontalStaffline === '1';
+        
+        // set the backendSelect debug controls dropdown menu selected item
+        //console.log("true: " + backendSelect && backendType.toLowerCase && backendType.toLowerCase() === "canvas");
+        // TODO somehow backendSelect becomes undefined here:
+        /*if (backendSelect && backendType.toLowerCase && backendType.toLowerCase() === "canvas") {
+            console.log("here1");
+            for (var i=0; i<backendSelect.options.length; i++) {
+                if (backendSelect.options[i].value.toLowerCase() === "canvas") {
+                    backendSelect.selectedIndex = i;
+                }
+            }
+            backendSelect.value = "Canvas";
+        }*/
+
+        divControls = document.getElementById('divControls');
+        zoomControls = document.getElementById('zoomControls');
+        header = document.getElementById('header');
         err = document.getElementById("error-td");
         error_tr = document.getElementById("error-tr");
-        zoomDiv = document.getElementById("zoom-str");
+        zoomDivs = [];
+        zoomDivs.push(document.getElementById("zoom-str"));
+        zoomDivs.push(document.getElementById("zoom-str-optional"));
         custom = document.createElement("option");
         selectSample = document.getElementById("selectSample");
         selectBounding = document.getElementById("selectBounding");
         skylineDebug = document.getElementById("skylineDebug");
         bottomlineDebug = document.getElementById("bottomlineDebug");
-        zoomIn = document.getElementById("zoom-in-btn");
-        zoomOut = document.getElementById("zoom-out-btn");
+        zoomIns = [];
+        zoomIns.push(document.getElementById("zoom-in-btn"));
+        zoomIns.push(document.getElementById("zoom-in-btn-optional"));
+        zoomOuts = [];
+        zoomOuts.push(document.getElementById("zoom-out-btn"));
+        zoomOuts.push(document.getElementById("zoom-out-btn-optional"));
         canvas = document.createElement("div");
+        if (horizontalScrolling) {
+            canvas.style.overflowX = 'auto'; // enable horizontal scrolling
+        }
+        //canvas.id = 'osmdCanvasDiv';
+        //canvas.style.overflowX = 'auto'; // enable horizontal scrolling
         nextCursorBtn = document.getElementById("next-cursor-btn");
         resetCursorBtn = document.getElementById("reset-cursor-btn");
+        followCursorCheckbox = document.getElementById("follow-cursor-checkbox");
         showCursorBtn = document.getElementById("show-cursor-btn");
         hideCursorBtn = document.getElementById("hide-cursor-btn");
         backendSelect = document.getElementById("backend-select");
+        backendSelectDiv = document.getElementById("backend-select-div");
         debugReRenderBtn = document.getElementById("debug-re-render-btn");
         debugClearBtn = document.getElementById("debug-clear-btn");
+        selectPageSizes = [];
+        selectPageSizes.push(document.getElementById("selectPageSize"));
+        selectPageSizes.push(document.getElementById("selectPageSize-optional"));
+        printPdfBtns = [];
+        printPdfBtns.push(document.getElementById("print-pdf-btn"));
+        printPdfBtns.push(document.getElementById("print-pdf-btn-optional"));
+
+        //var defaultDisplayVisibleValue = "block"; // TODO in some browsers flow could be the better/default value
+        var defaultVisibilityValue = "visible";
+        var devDemoRunning = process.env.OSMD_DEBUG_CONTROLS;
+        showDebugControls = paramDebugControls === '1' || (devDemoRunning && paramDebugControls !== '0')
+        if (showDebugControls) {
+            var elementsToEnable = [
+                selectSample, selectBounding, selectPageSize, backendSelect, backendSelectDiv, divControls
+            ];
+            for (var i=0; i<elementsToEnable.length; i++) {
+                if (elementsToEnable[i]) { // make sure this element is not null/exists in the index.html, e.g. github.io demo has different index.html
+                    if (elementsToEnable[i].style) {
+                        elementsToEnable[i].style.visibility = defaultVisibilityValue;
+                        elementsToEnable[i].style.opacity = 1.0;
+                    }
+                }
+            }
+        } else {
+            if (divControls) {
+                divControls.style.display = "none";
+            }
+        }
 
+        const optionalControls = document.getElementById('optionalControls');
+        if (optionalControls) {
+            if (showControls) {
+                optionalControls.style.visibility = defaultVisibilityValue;
+                optionalControls.style.opacity = 0.8;
+            } else {
+                optionalControls.style.display = 'none';
+            }
+        }
+
+        if (!showHeader) {
+            if (header) {
+                header.style.display = 'none';
+            }
+        } else {
+            if (header) {
+                header.style.opacity = 1.0;
+            }
+        }
         // Hide error
         error();
 
+        if (showControls) {
+            const optionalControls = document.getElementById('optionalControls');
+            if (optionalControls) {
+                optionalControls.style.opacity = 1.0;
+                // optionalControls.appendChild(zoomControlsButtons);
+                // optionalControls.appendChild(zoomControlsString);
+                optionalControls.style.position = 'absolute';
+                optionalControls.style.zIndex = '10';
+                optionalControls.style.right = '10px';
+                // optionalControls.style.padding = '10px';
+            }
+
+            if (showZoomControl) {
+                const zoomControlsButtonsColumn = document.getElementById('zoomControlsButtons-optional-column');
+                zoomControlsButtonsColumn.style.opacity = 1.0;
+                // const zoomControlsButtons = document.getElementById('zoomControlsButtons-optional');
+                // zoomControlsButtons.style.opacity = 1.0;
+                const zoomControlsString = document.getElementById('zoom-str-optional'); // actually === zoomDivs[1] above
+
+                if (zoomControlsString) {
+                    zoomControlsString.innerHTML = Math.floor(zoom * 100.0) + "%";
+                    zoomControlsString.style.display = 'inline';
+                    // zoomControlsString.style.padding = '10px';
+                }
+            }
+
+            if (showExportPdfControl) {
+                const exportPdfButtonColumn = document.getElementById('print-pdf-btn-optional-column');
+                if (exportPdfButtonColumn) {
+                    exportPdfButtonColumn.style.opacity = 1.0;
+                }
+            }
+
+            const pageFormatControlColumn = document.getElementById("selectPageSize-optional-column");
+            if (pageFormatControlColumn) {
+                if (showPageFormatControl) {
+                    pageFormatControlColumn.style.opacity = 1.0;
+                } else {
+                    // showPageFormatControlColumn.innerHTML = "";
+                    // pageFormatControlColumn.style.minWidth = 0;
+                    // pageFormatControlColumn.style.width = 0;
+                    pageFormatControlColumn.style.display = 'none'; // squeezes buttons/columns
+                    // pageFormatControlColumn.style.visibility = 'hidden';
+
+                    // const optionalControlsColumnContainer = document.getElementById("optionalControlsColumnContainer");
+                    // optionalControlsColumnContainer.removeChild(pageFormatControlColumn);
+                    // optionalControlsColumnContainer.width *= 0.66;
+                    // optionalControls.witdh *= 0.66;
+                    // optionalControls.focus();
+                }
+            }
+        }
+
         // Create select
         for (name in samples) {
             if (samples.hasOwnProperty(name)) {
@@ -95,47 +346,79 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
                 option.value = samples[name];
                 option.textContent = name;
             }
-            selectSample.appendChild(option);
+            if (selectSample) {
+                selectSample.appendChild(option);
+            }
+        }
+        if (selectSample) {
+            selectSample.onchange = selectSampleOnChange;
         }
-        selectSample.onchange = selectSampleOnChange;
         if (selectBounding) {
             selectBounding.onchange = selectBoundingOnChange;
         }
 
+        for (const selectPageSize of selectPageSizes) {
+            if (selectPageSize) {
+                selectPageSize.onchange = function (evt) {
+                    var value = evt.target.value;
+                    openSheetMusicDisplay.setPageFormat(value);
+                    openSheetMusicDisplay.render();
+                };
+            }
+        }
+
+        for (const printPdfBtn of printPdfBtns) {
+            if (printPdfBtn) {
+                printPdfBtn.onclick = function () {
+                    createPdf();
+                }
+            }
+        }
+
         // Pre-select default music piece
 
         custom.appendChild(document.createTextNode("Custom"));
 
         // Create zoom controls
-        zoomIn.onclick = function () {
-            zoom *= 1.2;
-            scale();
-        };
-        zoomOut.onclick = function () {
-            zoom /= 1.2;
-            scale();
-        };
+        for (const zoomIn of zoomIns) {
+            if (zoomIn) {
+                zoomIn.onclick = function () {
+                    zoom *= 1.2;
+                    scale();
+                };
+            }
+        }
+        for (const zoomOut of zoomOuts) {
+            if (zoomOut) {
+                zoomOut.onclick = function () {
+                    zoom /= 1.2;
+                    scale();
+                };
+            }
+        }
 
         if (skylineDebug) {
-            skylineDebug.onclick = function() {
+            skylineDebug.onclick = function () {
                 openSheetMusicDisplay.DrawSkyLine = !openSheetMusicDisplay.DrawSkyLine;
+                openSheetMusicDisplay.render();
             }
         }
 
         if (bottomlineDebug) {
-            bottomlineDebug.onclick = function() {
+            bottomlineDebug.onclick = function () {
                 openSheetMusicDisplay.DrawBottomLine = !openSheetMusicDisplay.DrawBottomLine;
+                openSheetMusicDisplay.render();
             }
         }
 
         if (debugReRenderBtn) {
-            debugReRenderBtn.onclick = function() {
+            debugReRenderBtn.onclick = function () {
                 rerender();
             }
         }
 
         if (debugClearBtn) {
-            debugClearBtn.onclick = function() {
+            debugClearBtn.onclick = function () {
                 openSheetMusicDisplay.clear();
             }
         }
@@ -143,17 +426,24 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
         // Create OSMD object and canvas
         openSheetMusicDisplay = new OpenSheetMusicDisplay(canvas, {
             autoResize: true,
-            backend: backendSelect.value,
+            backend: backendType,
+            //backend: "canvas",
             disableCursor: false,
-            drawingParameters: "default", // try compact (instead of default)
+            drawingParameters: compactMode ? "compact" : "default", // try compact (instead of default)
             drawPartNames: true, // try false
             // drawTitle: false,
             // drawSubtitle: false,
             drawFingerings: true,
-            fingeringPosition: "auto", // left is default. try right. experimental: auto, above, below.
+            fingeringPosition: "left", // left is default. try right. experimental: auto, above, below.
             // fingeringInsideStafflines: "true", // default: false. true draws fingerings directly above/below notes
             setWantedStemDirectionByXml: true, // try false, which was previously the default behavior
             // drawUpToMeasureNumber: 3, // draws only up to measure 3, meaning it draws measure 1 to 3 of the piece.
+            drawFromMeasureNumber : measureRangeStart,
+            drawUpToMeasureNumber : measureRangeEnd,
+
+            //drawMeasureNumbers: false, // disable drawing measure numbers
+            //measureNumberInterval: 4, // draw measure numbers only every 4 bars (and at the beginning of a new system)
+            useXMLMeasureNumbers: true, // read measure numbers from xml
 
             // coloring options
             coloringEnabled: true,
@@ -167,30 +457,42 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
                 //groups: [[3,4], [1,1]],
                 maintain_stem_directions: false
             },
+            pageFormat: pageFormat,
+            pageBackgroundColor: pageBackgroundColor,
+            renderSingleHorizontalStaffline: singleHorizontalStaffline
 
             // tupletsBracketed: true, // creates brackets for all tuplets except triplets, even when not set by xml
             // tripletsBracketed: true,
             // tupletsRatioed: true, // unconventional; renders ratios for tuplets (3:2 instead of 3 for triplets)
         });
-        openSheetMusicDisplay.setLogLevel('info');
+        openSheetMusicDisplay.setLogLevel('info'); // set this to 'debug' if you want to see more detailed control flow information in console
         document.body.appendChild(canvas);
 
-        window.addEventListener("keydown", function(e) {
+        window.addEventListener("keydown", function (e) {
             var event = window.event ? window.event : e;
             if (event.keyCode === 39) {
                 openSheetMusicDisplay.cursor.next();
             }
         });
-        nextCursorBtn.addEventListener("click", function() {
+        nextCursorBtn.addEventListener("click", function () {
             openSheetMusicDisplay.cursor.next();
         });
-        resetCursorBtn.addEventListener("click", function() {
+        resetCursorBtn.addEventListener("click", function () {
             openSheetMusicDisplay.cursor.reset();
         });
-        hideCursorBtn.addEventListener("click", function() {
-            openSheetMusicDisplay.cursor.hide();
+        if (followCursorCheckbox) {
+            followCursorCheckbox.onclick = function () {
+                openSheetMusicDisplay.FollowCursor = !openSheetMusicDisplay.FollowCursor;
+            }
+        }
+        hideCursorBtn.addEventListener("click", function () {
+            if (openSheetMusicDisplay.cursor) {
+                openSheetMusicDisplay.cursor.hide();
+            } else {
+                console.info("Can't hide cursor, as it was disabled (e.g. by drawingParameters).");
+            }
         });
-        showCursorBtn.addEventListener("click", function() {
+        showCursorBtn.addEventListener("click", function () {
             if (openSheetMusicDisplay.cursor) {
                 openSheetMusicDisplay.cursor.show();
             } else {
@@ -198,23 +500,81 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
             }
         });
 
-        backendSelect.addEventListener("change", function(e) {
+        backendSelect.addEventListener("change", function (e) {
             var value = e.target.value;
             var createNewOsmd = true;
 
             if (createNewOsmd) {
                 // clears the canvas element
                 canvas.innerHTML = "";
-                openSheetMusicDisplay = new OpenSheetMusicDisplay(canvas, {backend: value});
-                openSheetMusicDisplay.setLogLevel('info');
+                openSheetMusicDisplay = new OpenSheetMusicDisplay(canvas, { backend: value });
+                openSheetMusicDisplay.setLogLevel('info'); // set this to 'debug' if you want to get more detailed control flow information
             } else {
                 // alternative, doesn't work yet, see setOptions():
-                openSheetMusicDisplay.setOptions({backend: value});
+                openSheetMusicDisplay.setOptions({ backend: value });
             }
+            console.log("[OSMD] selectSampleOnChange addEventListener change");
+            // selectSampleOnChange();
+        });
 
+        // TODO after selectSampleOnChange, the resize handler triggers immediately,
+        //   so we render twice at the start of the demo.
+        //   maybe delay the first osmd render, e.g. when window ready?
+        if (paramOpenUrl) {
+            if (openSheetMusicDisplay.getLogLevel() < 2) { // debug or trace
+                console.log("[OSMD] selectSampleOnChange with " + paramOpenUrl);
+            }
+            // DEBUG: cause an error for a certain sample, for testing
+            // if (paramOpenUrl.startsWith("Beethoven")) {
+            //     paramOpenUrl.causeError();
+            // }
+            selectSampleOnChange(paramOpenUrl);
+        } else {
+            if (openSheetMusicDisplay.getLogLevel() < 2) { // debug or trace
+                console.log("[OSMD] selectSampleOnChange without param");
+            }
             selectSampleOnChange();
+        }
+    }
 
-        });
+    function findGetParameter(parameterName) {
+        // special treatment for the openUrl parameter, because different systems attach different arguments to an URL.
+        // because of CORS (cross-origin safety restrictions), you can only load an xml file from the same origin (server).
+
+        // test parameter: ?openUrl=https://opensheetmusiceducation.org/index.php?gf-download=2020%2F01%2FJohannSebastianBach_PraeludiumInCDur_BWV846_1.xml&endUrl&form-id=1&field-id=4&hash=c4ba271ef08204a26cbd4cd2d751c53b78f238c25ddbb1f343e1172f2ce2aa53
+        //   (enable the console.log at the end of this method for testing)
+        // working test parameter in local demo: ?openUrl=OSMD_function_test_all.xml&endUrl
+    
+        if (parameterName === 'openUrl') {
+            let startParameterName = 'openUrl=';
+            let endParameterName = '&endUrl';
+            let openUrlIndex = location.search.indexOf(startParameterName);
+            if (openUrlIndex < 0) {
+                return undefined;
+            }
+            let endIndex = location.search.indexOf(endParameterName) + endParameterName.length;
+            if (endIndex < 0) {
+                console.log("[OSMD] If using openUrl as a parameter, you have to end it with '&endUrl'. openUrl parameter omitted.");
+                return undefined;
+            }
+            let urlString = location.search.substring(openUrlIndex + startParameterName.length, endIndex - endParameterName.length);
+            //console.log("openUrl: " + urlString);
+            return urlString;
+        }
+
+        let result = undefined;
+        let tmp = [];
+        location.search
+            .substr(1)
+            .split('&')
+            .forEach(function (item) {
+                tmp = item.split('=');
+                if (tmp[0] === parameterName) {
+                    result = decodeURIComponent(tmp[1]);
+                    //console.log('Found param:' + parameterName + ' = ' + result);
+                }
+            });
+        return result;
     }
 
     function selectBoundingOnChange(evt) {
@@ -227,28 +587,132 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
         disable();
         var isCustom = typeof str === "string";
         if (!isCustom) {
-            str = sampleFolder + selectSample.value;
+            if (selectSample) {
+                str = sampleFolder + selectSample.value;
+            } else {
+                if (samples && samples.length > 0) {
+                    str = sampleFolder + samples[0];
+                } else {
+                    return; // no sample to load right now
+                }
+            }
         }
-        zoom = 1.0;
+        // zoom = 1.0;
+
+        setSampleSpecificOptions(str, isCustom);
+
         openSheetMusicDisplay.load(str).then(
-            function() {
-                // This gives you access to the osmd object in the console. Do not use in productive code
+            function () {
+                // This gives you access to the osmd object in the console. Do not use in production code
                 window.osmd = openSheetMusicDisplay;
+                openSheetMusicDisplay.zoom = zoom;
                 return openSheetMusicDisplay.render();
             },
-            function(e) {
+            function (e) {
                 errorLoadingOrRenderingSheet(e, "rendering");
             }
         ).then(
-            function() {
+            function () {
                 return onLoadingEnd(isCustom);
-            }, function(e) {
+            }, function (e) {
                 errorLoadingOrRenderingSheet(e, "loading");
                 onLoadingEnd(isCustom);
             }
         );
     }
 
+    function setSampleSpecificOptions(str, isCustom) {
+        if (!isCustom && str.includes("measuresToDraw")) { // set options for measuresToDraw sample
+            // stash previously set range of measures to draw
+            if (!measureToDrawRangeNeedsReset) { // only stash once, when measuresToDraw called multiple times in a row
+                minMeasureToDrawStashed = openSheetMusicDisplay.EngravingRules.MinMeasureToDrawIndex + 1;
+                maxMeasureToDrawStashed = openSheetMusicDisplay.EngravingRules.MaxMeasureToDrawIndex + 1;
+            }
+            measureToDrawRangeNeedsReset = true;
+
+            // for debugging: draw from a random range of measures
+            let minMeasureToDraw = Math.ceil(Math.random() * 15); // measures start at 1 (measureIndex = measure number - 1 elsewhere)
+            let maxMeasureToDraw = Math.ceil(Math.random() * 15);
+            if (minMeasureToDraw > maxMeasureToDraw) {
+                minMeasureToDraw = maxMeasureToDraw;
+                let a = minMeasureToDraw;
+                maxMeasureToDraw = a;
+            }
+            //minMeasureToDraw = 1; // set your custom indexes here. Drawing only one measure can be a special case
+            //maxMeasureToDraw = 1;
+            console.log("drawing measures in the range: [" + minMeasureToDraw + "," + maxMeasureToDraw + "]");
+            openSheetMusicDisplay.setOptions({
+                drawFromMeasureNumber: minMeasureToDraw,
+                drawUpToMeasureNumber: maxMeasureToDraw
+            });
+        } else if (measureToDrawRangeNeedsReset) { // reset for other samples
+            openSheetMusicDisplay.setOptions({
+                drawFromMeasureNumber: minMeasureToDrawStashed,
+                drawUpToMeasureNumber: maxMeasureToDrawStashed
+            });
+            measureToDrawRangeNeedsReset = false;
+        }
+
+        if (!isCustom && str.includes("Test_Container_height")) {
+            drawingParametersStashed = openSheetMusicDisplay.drawingParameters.drawingParametersEnum;
+            openSheetMusicDisplay.setOptions({
+                drawingParameters: "compacttight"
+            });
+            drawingParametersNeedsReset = true;
+        } else if (drawingParametersNeedsReset) {
+            openSheetMusicDisplay.setOptions({
+                drawingParameters: drawingParametersStashed
+            });
+            drawingParametersNeedsReset = false;
+        }
+
+        // Enable Boomwhacker-like coloring for OSMD Function Test - Auto-Coloring (Boomwhacker-like, custom color set)
+        if (!isCustom && str.includes("auto-custom-coloring")) { // set options for auto coloring sample
+            autoCustomColoringOptionNeedsReset = true;
+            //openSheetMusicDisplay.setOptions({coloringMode: 1}); // Auto-Coloring with pre-defined colors
+            openSheetMusicDisplay.setOptions({
+                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) {
+            openSheetMusicDisplay.setOptions({ // set default values. better would be to restore to stashed values, but unnecessarily complex for demo
+                coloringMode: 0,
+                colorStemsLikeNoteheads: false,
+                coloringSetCustom: null
+            });
+            autoCustomColoringOptionNeedsReset = false;
+        }
+        if (!isCustom && str.includes("autobeam")) {
+            autobeamOptionStashedValue = openSheetMusicDisplay.EngravingRules.AutoBeamNotes; // stash previously set value, to restore later
+            autobeamOptionNeedsReset = true;
+            openSheetMusicDisplay.setOptions({ autoBeam: true });
+        } else if (autobeamOptionNeedsReset) {
+            openSheetMusicDisplay.setOptions({ autoBeam: autobeamOptionStashedValue });
+            autobeamOptionNeedsReset = false;
+        }
+        if (!isCustom && str.includes("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;
+            openSheetMusicDisplay.setOptions({ drawPartNames: false, drawPartAbbreviations: false }); // TODO sets osmd.drawingParameters.DrawPartNames! also check EngravingRules.RenderPartAbbreviations, was false
+            drawPartNamesOptionNeedsReset = true;
+        } else if (drawPartNamesOptionNeedsReset) {
+            openSheetMusicDisplay.setOptions({ drawPartNames: drawPartNamesOptionStashedValue, drawPartAbbreviations: drawPartAbbreviationsStashedValue });
+            drawPartNamesOptionNeedsReset = false;
+        }
+    }
+
     function errorLoadingOrRenderingSheet(e, loadingOrRenderingString) {
         var errorString = "Error " + loadingOrRenderingString + " sheet: " + e;
         // if (process.env.DEBUG) { // people may not set a debug environment variable for the demo.
@@ -269,13 +733,17 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
     }
 
     function logCanvasSize() {
-        zoomDiv.innerHTML = Math.floor(zoom * 100.0) + "%";
+        for (const zoomDiv of zoomDivs) {
+            if (zoomDiv) {
+                zoomDiv.innerHTML = Math.floor(zoom * 100.0) + "%";
+            }
+        }
     }
 
     function scale() {
         disable();
-        window.setTimeout(function(){
-            openSheetMusicDisplay.zoom = zoom;
+        window.setTimeout(function () {
+            openSheetMusicDisplay.Zoom = zoom;
             openSheetMusicDisplay.render();
             enable();
         }, 0);
@@ -283,10 +751,11 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
 
     function rerender() {
         disable();
-        window.setTimeout(function(){
+        window.setTimeout(function () {
             if (openSheetMusicDisplay.IsReadyToRender()) {
                 openSheetMusicDisplay.render();
             } else {
+                console.log("[OSMD demo] Loses context!"); // TODO not sure that this message is reasonable, renders fine anyways. maybe vexflow context lost?
                 selectSampleOnChange(); // reload sample e.g. after osmd.clear()
             }
             enable();
@@ -297,6 +766,7 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
         if (!errString) {
             error_tr.style.display = "none";
         } else {
+            console.log("[OSMD demo] error: " + errString)
             err.textContent = errString;
             error_tr.style.display = "";
             canvas.width = canvas.height = 0;
@@ -307,30 +777,99 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
     // Enable/Disable Controls
     function disable() {
         document.body.style.opacity = 0.3;
-        selectSample.disabled = zoomIn.disabled = zoomOut.disabled = "disabled";
+        setDisabledForControls("disabled");
     }
+
     function enable() {
         document.body.style.opacity = 1;
-        selectSample.disabled = zoomIn.disabled = zoomOut.disabled = "";
+        setDisabledForControls("");
         logCanvasSize();
     }
 
+    function setDisabledForControls(disabledValue) {
+        if (selectSample) {
+            selectSample.disabled = disabledValue;
+        }
+        for (const zoomIn of zoomIns) {
+            if (zoomIn) {
+                zoomIn.disabled = disabledValue;
+            }
+        }
+        for (const zoomOut of zoomOuts) {
+            if (zoomOut) {
+                zoomOut.disabled = disabledValue;
+            }
+        }
+    }
+
+    /**
+     * Creates a Pdf of the currently rendered MusicXML
+     * @param pdfName if no name is given, the composer and title of the piece will be used
+     */
+    function createPdf(pdfName) {
+        if (openSheetMusicDisplay.backendType !== BackendType.SVG) {
+            console.log("[OSMD] createPdf(): Warning: createPDF is only supported for SVG background for now, not for Canvas." +
+                " Please use osmd.setOptions({backendType: SVG}).");
+            return;
+        }
+
+        if (pdfName === undefined) {
+            pdfName = openSheetMusicDisplay.sheet.FullNameString + ".pdf";
+        }
+
+        const backends = openSheetMusicDisplay.drawer.Backends;
+        let svgElement = backends[0].getSvgElement();
+
+        let pageWidth = 210;
+        let pageHeight = 297;
+        const engravingRulesPageFormat = openSheetMusicDisplay.rules.PageFormat;
+        if (engravingRulesPageFormat && !engravingRulesPageFormat.IsUndefined) {
+            pageWidth = engravingRulesPageFormat.width;
+            pageHeight = engravingRulesPageFormat.height;
+        } else {
+            pageHeight = pageWidth * svgElement.clientHeight / svgElement.clientWidth;
+        }
+
+        const orientation = pageHeight > pageWidth ? "p" : "l";
+        // create a new jsPDF instance
+        const pdf = new jsPDF(orientation, "mm", [pageWidth, pageHeight]);
+        const scale = pageWidth / svgElement.clientWidth;
+        for (let idx = 0, len = backends.length; idx < len; ++idx) {
+            if (idx > 0) {
+                pdf.addPage();
+            }
+            svgElement = backends[idx].getSvgElement();
+
+            // render the svg element
+            svg2pdf(svgElement, pdf, {
+                scale: scale,
+                xOffset: 0,
+                yOffset: 0
+            });
+        }
+
+        // simply save the created pdf
+        pdf.save(pdfName);
+
+        // note that using jspdf with svg2pdf creates unnecessary console warnings "AcroForm-Classes are not populated into global-namespace..."
+        // this will hopefully be fixed with a new jspdf release, see https://github.com/yWorks/jsPDF/pull/32
+    }
+
     // Register events: load, drag&drop
-    window.addEventListener("load", function() {
+    window.addEventListener("load", function () {
         init();
-        selectSampleOnChange();
     });
-    window.addEventListener("dragenter", function(event) {
+    window.addEventListener("dragenter", function (event) {
         event.preventDefault();
         disable();
     });
-    window.addEventListener("dragover", function(event) {
+    window.addEventListener("dragover", function (event) {
         event.preventDefault();
     });
-    window.addEventListener("dragleave", function(event) {
+    window.addEventListener("dragleave", function (event) {
         enable();
     });
-    window.addEventListener("drop", function(event) {
+    window.addEventListener("drop", function (event) {
         event.preventDefault();
         if (!event.dataTransfer || !event.dataTransfer.files || event.dataTransfer.files.length === 0) {
             return;
@@ -347,7 +886,7 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
         if (filename.toLowerCase().indexOf(".xml") > 0
             || filename.toLowerCase().indexOf(".musicxml") > 0) {
             reader.readAsText(event.dataTransfer.files[0]);
-        } else if (event.dataTransfer.files[0].name.toLowerCase().indexOf(".mxl") > 0){
+        } else if (event.dataTransfer.files[0].name.toLowerCase().indexOf(".mxl") > 0) {
             reader.readAsBinaryString(event.dataTransfer.files[0]);
         }
         else {

+ 0 - 429
external/vexflow/vexflow.d.ts

@@ -1,429 +0,0 @@
-
-
-declare namespace Vex {
-
-    export module Flow {
-        const RESOLUTION: any;
-        const DEFAULT_NOTATION_FONT_SCALE: number;
-
-        export class Formatter {
-            constructor();
-
-            public hasMinTotalWidth: boolean;
-            public minTotalWidth: number;
-
-            public joinVoices(voices: Voice[]): void;
-
-            public format(voices: Voice[], width: number, options?: any): void;
-
-            public preCalculateMinTotalWidth(voices: Voice[]): number;
-        }
-
-        export class BoundingBox {
-            constructor(x: number, y: number, w: number, h: number);
-
-            public mergeWith(bb: BoundingBox): BoundingBox;
-
-            public x: number;
-
-            public y: number;
-
-            public w: number;
-
-            public h: number;
-
-            public draw(ctx: Vex.Flow.RenderContext): void;
-        }
-
-        export class Tickable {
-            public reset(): void;
-
-            public setStave(stave: Stave);
-
-            public getBoundingBox(): BoundingBox;
-
-            public getAttribute(arg: string): string;
-        }
-
-        export class Voice {
-            constructor(time: any);
-
-            public static Mode: any;
-
-            public context: RenderContext;
-
-            public tickables: Tickable[];
-
-            public getBoundingBox(): BoundingBox;
-
-            public setStave(stave: Stave): Voice;
-
-            public addTickables(tickables: Tickable[]): Voice;
-
-            public addTickable(tickable: Tickable): Voice;
-
-            public setMode(mode: any): Voice;
-
-            public draw(ctx: any, stave: Stave): void;
-        }
-
-        export class Note extends Tickable {
-            public addStroke(index: number, stroke: Stroke): void;
-        }
-
-        class NoteHead extends Note {
-            constructor(head_options: Object);
-            static CATEGORY(): string;
-        }
-
-        export class TextBracket {
-            constructor(note_struct: any);
-            
-            public setContext(ctx: RenderContext): TextBracket;
-
-            public draw(): void;
-
-        }
-
-        export class TextNote extends Note {
-            constructor(note_struct: any);
-            
-            public setContext(ctx: RenderContext): TextBracket;
-
-            public draw(): void;
-        }
-
-        export class Stem {
-            public static UP: number;
-            public static DOWN: number;
-        }
-        export class StemmableNote extends Note {
-            public getStemDirection(): number;
-            public setStemDirection(direction: number): StemmableNote;
-            public x_shift: number;
-            public getAbsoluteX(): number;
-            public addModifier(index: number, modifier: Modifier): StemmableNote;
-            public preFormatted: boolean;
-        }
-
-        export class GhostNote extends StemmableNote {
-            constructor(note_struct: any);
-            public setStave(stave): void;
-        }
-
-        export class StaveNote extends StemmableNote {
-            constructor(note_struct: any);
-
-            public getNoteHeadBounds(): any;
-
-            public getNoteHeadBeginX(): number;
-
-            public getNoteHeadEndX(): number;
-
-            public getGlyphWidth(): number;
-
-            public addAccidental(index: number, accidental: Accidental): StaveNote;
-
-            public addAnnotation(index: number, annotation: Annotation): StaveNote;
-
-            public addDotToAll(): void;
-
-            public addModifier(index: number, modifier: Modifier): StaveNote;
-
-            public setStyle(style: any): void;
-            public setStemStyle(style: any): void;
-            public setFlagStyle(style: any): void;
-
-            public getKeyProps(): Object[];
-
-            // temp solution until noteheadStyles PR is through
-            public note_heads: any; // NoteHead[]; 
-            public flag: Element;
-            public beam: Beam;
-        }
-
-        export class GraceNote extends StaveNote {
-            static SCALE: number;
-            static LEDGER_LINE_OFFSET: number;
-            constructor(note_struct: any);
-        }
-
-        export class GraceNoteGroup extends Modifier {
-            constructor(grace_notes: GraceNote[], show_slur: boolean);
-            public beamNotes(): GraceNoteGroup;
-        }
-
-        export class StaveTie {
-            constructor(notes_struct: any);
-
-            public setContext(ctx: RenderContext): StaveTie;
-
-            public draw(): void;
-        }
-
-        export class Stave {
-            constructor(x: number, y: number, width: number, options: any);
-
-            public setX(x: number): Stave;
-
-            public setY(y: number): Stave;
-
-            public getX(): number;
-
-            public setBegBarType(type: any): Stave;
-
-            public setEndBarType(type: any): Stave;
-
-            public addClef(clefSpec: string, size: any, annotation: any, position: any): void;
-
-            public setEndClef(clefSpec: string, size: any, annotation: any): void;
-
-            public getModifiers(): StaveModifier[];
-
-            public getYForGlyphs(): number;
-
-            public getWidth(): number;
-
-            public setWidth(width: number): Stave;
-
-            public getNoteStartX(): number;
-
-            public getModifierXShift(): number;
-
-            public getNoteEndX(): number;
-
-            public setNoteStartX(x: number): Stave;
-
-            public setKeySignature(keySpec: any, cancelKeySpec: any, position: any): Stave;
-
-            public setText(text: string, position: number, options: any): void;
-
-            public format(): void;
-
-            public getSpacingBetweenLines(): number;
-
-            public getNumLines(): number;
-
-            public getLineForY(y: number): number;
-
-            public getYForLine(y: number): number;
-
-            public getModifiers(pos: any, cat: any): Clef[]; // FIXME
-
-            public setContext(ctx: RenderContext): Stave;
-
-            public addModifier(mod: any, pos: any): void;
-
-            public draw(): void;
-
-            public addTimeSignature(sig: string): void;
-
-            public setVoltaType(type: number, number_t: number, y: number): void;
-
-            public setTempo(tempo: Object, y: number): Stave;
-
-            public setShiftX(x: number): Stave;
-        }
-
-        export class StaveTempo extends StaveModifier { // needs Vexflow PR to be exported/usable
-            constructor(tempo: Object, x: number, shift_y: number);
-        }
-
-        export class Volta extends StaveModifier {
-            public static type: any;
-        }
-
-        export class Modifier {
-            public static Position: any;
-
-            public getCategory(): string;
-
-            public getWidth(): number;
-
-            public getPadding(index: number): number;
-
-            public getPosition(): number;
-
-            public setPosition(position: number): Modifier;
-
-            public setIndex(index: number): void;
-        }
-
-        export class FretHandFinger extends Modifier {
-            constructor(finger: string);
-        }
-
-        export class StringNumber extends Modifier {
-            constructor(string: string);
-            setOffsetY(value: number);
-        }
-        
-        export class Stroke extends Modifier {
-            constructor(type: number);
-            public static Type: any; // unreliable values, use Arpeggio.ArpeggioType instead
-        }
-
-        export class NoteSubGroup extends Modifier {
-            constructor(notes: Object);
-        }
-
-        export class StaveModifier extends Modifier {
-            public getPosition(): number;
-
-        }
-
-        export class Repetition extends StaveModifier {
-            constructor(type: any, x: number, y_shift: number);
-        }
-
-        export class Clef extends StaveModifier {
-            constructor(type: string, size: string, annotation: string);
-
-            public static category: string;
-            public static types: { [type: string]: any; };
-            public glyph: any;
-            public x: number;
-            public stave: Stave;
-
-            public getBoundingBox(): BoundingBox;
-
-            public setStave(stave: Stave): void;
-        }
-        
-        export class ClefNote  extends Note {
-            constructor(type: string, size: string, annotation: string);
-
-            public type: string;
-        }
-
-        export class Renderer {
-            constructor(canvas: HTMLElement, backend: number);
-
-            public static Backends: {
-                CANVAS: number,
-                RAPHAEL: number,
-                SVG: number,
-                VML: number
-            };
-
-            public resize(a: number, b: number): void;
-
-            public getContext(): CanvasContext | SVGContext;
-        }
-
-        export class TimeSignature extends StaveModifier {
-            constructor(timeSpec: string, customPadding?: any);
-        }
-        export class KeySignature extends StaveModifier {
-            constructor(keySpec: string, cancelKeySpec: string, alterKeySpec?: string);
-        }
-
-        export class Accidental {
-            constructor(type: string);
-        }
-
-        export class Annotation {
-            constructor(type: string);
-        }
-
-        export class Articulation extends Modifier {
-            constructor(type: string);
-        }
-
-        export class Ornament extends Modifier {
-            constructor(type: string);
-            setDelayed(delayed: boolean): void;
-            setUpperAccidental(acc: string): void;
-            setLowerAccidental(acc: string): void;
-        }
-
-        export class Tremolo extends Modifier {
-            constructor(numberOfSlashes: number);
-        }
-        
-        export class Beam {
-            constructor(notes: StaveNote[], auto_stem: boolean);
-
-            public setContext(ctx: RenderContext): Beam;
-            public draw(): void;
-            public static generateBeams(notes: Vex.Flow.StemmableNote[], optionsObject?: any): Beam[];
-            public setStyle(style: any): void;
-        }
-
-        export class Fraction { // Vex.Flow.Fraction, used for generateBeams
-            constructor(nominator: number, denominator: number);
-        }
-
-        export class Tuplet {
-            constructor(notes: StaveNote[], options: any);
-
-            public setContext(ctx: RenderContext): Tuplet;
-
-            public draw(): void;
-        }
-
-        // interface for class Curve to draw slurs. The options are set to undefined
-        export class Curve {
-            constructor(from: StemmableNote, to: StemmableNote, options: any);
-            
-            public setContext(ctx: RenderContext): Curve;
-
-            public draw(): void;
-        }
-
-        export class RenderContext {
-            public scale(x: number, y: number): RenderContext;
-            public fillRect(x: number, y: number, width: number, height: number): RenderContext
-            public fillText(text: string, x: number, y: number): RenderContext;
-            public setFont(family: string, size: number, weight: string): RenderContext;
-            public beginPath(): RenderContext;
-            public moveTo(x, y): RenderContext;
-            public lineTo(x, y): RenderContext;
-            public bezierCurveTo(cp1_x: number, cp1_y: number, cp2_x: number, cp2_y: number, end_x: number, end_y: number): RenderContext;
-            public closePath(): RenderContext;
-            public stroke(): RenderContext;
-            public fill(): RenderContext;
-            public save(): RenderContext;
-            public restore(): RenderContext;
-            public lineWidth: number;
-        }
-
-        export class CanvasContext extends RenderContext {
-            public vexFlowCanvasContext: CanvasRenderingContext2D;
-        }
-
-        export class SVGContext extends RenderContext {
-            public svg: SVGElement;
-            public attributes: any;
-            public state: any;
-        }
-
-        export class StaveConnector {
-            constructor(top: Stave, bottom: Stave);
-
-            public static type: any;
-
-            public setType(type: any): StaveConnector;
-
-            public setContext(ctx: RenderContext): StaveConnector;
-
-            public setXShift(shift: number): StaveConnector;
-
-            public top_stave: Stave;
-
-            public bottom_stave: Stave;
-
-            public thickness: number;
-
-            public width: number;
-
-            public x_shift: number;
-
-            public draw(): void;
-        }
-    }
-}
-
-declare module "vexflow" {
-    export = Vex;
-}

+ 7 - 2
karma.conf.js

@@ -15,6 +15,10 @@ module.exports = function (config) {
 
         files: [
             {
+                pattern: 'test/Util/*.ts',
+                included: false
+            },
+            {
                 pattern: 'test/**/*.ts',
                 included: true
             }, {
@@ -28,7 +32,8 @@ module.exports = function (config) {
                 included: false,
                 watched: false,
                 served: true
-            }],
+            }
+        ],
 
         // preprocess matching files before serving them to the browser
         // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
@@ -53,7 +58,7 @@ module.exports = function (config) {
             resolve: common.resolve
         },
 
-        // Required for Firefox and Chorme to work
+        // Required for Firefox and Chrome to work
         // see https://github.com/webpack-contrib/karma-webpack/issues/188
         mime: {
             'text/x-typescript': ['ts']

+ 55 - 39
package.json

@@ -1,16 +1,17 @@
 {
   "name": "opensheetmusicdisplay",
-  "version": "0.6.8",
+  "version": "0.8.7",
   "description": "An open source JavaScript engine for displaying MusicXML based on VexFlow.",
   "main": "build/opensheetmusicdisplay.min.js",
   "typings": "build/dist/src/",
   "scripts": {
-    "docs": "typedoc --out ./build/docs --name OpenSheetMusicDisplay --module commonjs --target ES5 --ignoreCompilerErrors --mode file ./src",
+    "docs": "typedoc --out ./build/docs --name OpenSheetMusicDisplay --module commonjs --target ES2017 --ignoreCompilerErrors --mode file ./src",
     "eslint": "eslint .",
     "tslint": "tslint --project tsconfig.json \"src/**/*.ts\" \"test/**/*.ts\"",
-    "lint": "npm-run-all eslint tslint",
+    "lint": "npm-run-all tslint eslint",
     "test": "karma start --single-run --no-auto-watch",
     "test:watch": "karma start --no-single-run --auto-watch --browsers ChromeNoSecurity",
+    "prebuild": "ncp src/VexFlowPatch/src/ node_modules/vexflow/src/",
     "prepare": "npm run build",
     "build": "npm-run-all lint build:webpack",
     "build:doc": "cross-env STATIC_FILES_SUBFOLDER=sheets npm run build",
@@ -18,7 +19,20 @@
     "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",
-    "fix-memory-limit": "increase-memory-limit"
+    "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",
+    "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 --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 ^Beethoven --osmdtestingsingle",
+    "generate:blessed": "node ./test/Util/generateImages_browserless.js ../../build ./test/data ./visual_regression/blessed 0 0 allSmall --osmdtesting",
+    "test:visual": "bash ./test/Util/visual_regression.sh ./visual_regression",
+    "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": [
     "lint"
@@ -42,58 +56,60 @@
     "musicxml"
   ],
   "author": "PhonicScore",
-  "license": "MIT",
+  "license": "BSD-3-Clause",
   "bugs": {
     "url": "https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues"
   },
   "homepage": "http://opensheetmusicdisplay.org",
   "dependencies": {
-    "es6-promise": "^4.2.5",
-    "jszip": "^3.0.0",
-    "loglevel": "^1.5.0",
-    "typescript-collections": "^1.1.2",
-    "vexflow": "^1.2.87"
+    "@types/vexflow": "^3.0.0",
+    "jszip": "3.4.0",
+    "loglevel": "^1.6.8",
+    "typescript-collections": "^1.3.3",
+    "vexflow": "1.2.93"
   },
   "devDependencies": {
-    "@types/chai": "^4.0.3",
-    "@types/loglevel": "^1.4.29",
-    "@types/mocha": "^5.2.3",
+    "@types/chai": "^4.2.11",
+    "@types/mocha": "^7.0.2",
+    "@types/node": "^14.0.9",
+    "canvas": "^2.6.1",
     "chai": "^4.1.0",
-    "clean-webpack-plugin": "^1.0.1",
-    "cross-env": "^5.1.3",
-    "cz-conventional-changelog": "^2.0.0",
-    "eslint": "^5.13.0",
-    "eslint-config-standard": "^12.0.0",
-    "eslint-plugin-import": "^2.16.0",
-    "eslint-plugin-node": "^8.0.0",
-    "eslint-plugin-promise": "^4.0.1",
+    "clean-webpack-plugin": "^3.0.0",
+    "cross-blob": "^1.2.0",
+    "cross-env": "^7.0.2",
+    "cz-conventional-changelog": "^3.0.0",
+    "eslint": "^6.8.0",
+    "eslint-config-standard": "14.1.1",
+    "eslint-plugin-import": "^2.20.2",
+    "eslint-plugin-node": "^11.0.0",
+    "eslint-plugin-promise": "^4.2.1",
     "eslint-plugin-standard": "^4.0.0",
-    "html-webpack-plugin": "^3.2.0",
-    "http-server": "^0.11.0",
-    "increase-memory-limit": "^1.0.6",
-    "jquery": "^3.2.1",
-    "karma": "^4.0.0",
+    "html-webpack-plugin": "^4.3.0",
+    "jquery": "^3.4.1",
+    "jsdom": "^16.2.2",
+    "jspdf-yworks": "^2.1.1",
+    "karma": "^5.0.8",
     "karma-base64-to-js-preprocessor": "^0.0.1",
     "karma-chai": "^0.1.0",
-    "karma-chrome-launcher": "^2.2.0",
+    "karma-chrome-launcher": "^3.1.0",
     "karma-firefox-launcher": "^1.0.0",
-    "karma-mocha": "^1.1.1",
+    "karma-mocha": "^2.0.1",
     "karma-mocha-reporter": "^2.0.4",
-    "karma-webpack": "^3.0.4",
+    "karma-webpack": "^4.0.2",
     "karma-xml2js-preprocessor": "^0.0.3",
-    "mocha": "^5.2.0",
+    "mocha": "^7.0.1",
+    "ncp": "^2.0.0",
     "npm-run-all": "^4.1.2",
     "pre-commit": "^1.2.2",
+    "svg2pdf.js": "^1.5.0",
     "ts-loader": "^4.1.0",
-    "tslint": "^5.8.0",
-    "tslint-loader": "^3.5.3",
-    "typedoc": "^0.14.2",
-    "typescript": "^2.6.1",
-    "uglifyjs-webpack-plugin": "^2.1.1",
-    "underscore-template-loader": "^1.0.0",
-    "webpack": "^4.25.0",
-    "webpack-cli": "^3.0.8",
-    "webpack-dev-server": "^3.1.14",
+    "tslint": "^5.14.0",
+    "tslint-loader": "^3.5.4",
+    "typedoc": "^0.17.3",
+    "typescript": "^3.9.5",
+    "webpack": "^4.43.0",
+    "webpack-cli": "^3.3.11",
+    "webpack-dev-server": "^3.10.3",
     "webpack-merge": "^4.1.2",
     "webpack-visualizer-plugin": "^0.1.11"
   },

+ 29 - 6
src/Common/DataObjects/Fraction.ts

@@ -64,7 +64,7 @@ export class Fraction {
       return 1;
     }
 
-    while (b !== 0) {
+    while (Math.abs(b) > 1e-8) { // essentially b > 0, accounts for floating point inaccuracies (0.000...01)
       if (a > b) {
         a -= b;
       } else {
@@ -72,7 +72,7 @@ export class Fraction {
       }
     }
 
-    return a;
+    return Math.round(a); // prevent returning 4.000001 or something, though it doesn't happen for our samples
   }
 
   /**
@@ -181,7 +181,7 @@ export class Fraction {
    * Use Fraction.plus() for creating a new Fraction object being the sum of two Fractions.
    * @param fraction the Fraction to add.
    */
-  public Add(fraction: Fraction): void {
+  public Add(fraction: Fraction): Fraction {
     // normally should check if denominator or fraction.denominator is 0 but in our case
     // a zero denominator doesn't make sense
     this.numerator = (this.wholeValue * this.denominator + this.numerator) * fraction.denominator +
@@ -190,6 +190,7 @@ export class Fraction {
     this.wholeValue = 0;
     this.simplify();
     this.setRealValue();
+    return this;
   }
 
   /**
@@ -198,7 +199,7 @@ export class Fraction {
    * Use Fraction.minus() for creating a new Fraction object being the difference of two Fractions.
    * @param fraction the Fraction to subtract.
    */
-  public Sub(fraction: Fraction): void {
+  public Sub(fraction: Fraction): Fraction {
     // normally should check if denominator or fraction.denominator is 0 but in our case
     // a zero denominator doesn't make sense
     this.numerator = (this.wholeValue * this.denominator + this.numerator) * fraction.denominator -
@@ -207,6 +208,7 @@ export class Fraction {
     this.wholeValue = 0;
     this.simplify();
     this.setRealValue();
+    return this;
   }
   /**
    * Brute Force quanization by searching incremental with the numerator until the denominator is
@@ -239,7 +241,7 @@ export class Fraction {
   }
 
   public Equals(obj: Fraction): boolean {
-    return this.realValue === obj.realValue;
+    return this.realValue === obj?.realValue;
   }
 
   public CompareTo(obj: Fraction): number {
@@ -256,6 +258,14 @@ export class Fraction {
     return this.realValue <= frac.realValue;
   }
 
+  public gt(frac: Fraction): boolean {
+    return !this.lte(frac);
+  }
+
+  public gte(frac: Fraction): boolean {
+    return !this.lt(frac);
+  }
+
   //public Equals(f: Fraction): boolean {
   //    if (ReferenceEquals(this, f))
   //        return true;
@@ -302,6 +312,19 @@ export class Fraction {
     }
   }
 
+  public static FloatInaccuracyTolerance: number = 0.0001; // inaccuracy allowed when comparing Fraction.RealValues, because of floating point inaccuracy
+
+  public isOnBeat(timeSignature: Fraction): boolean { // use sourceMeasure.ActiveTimeSignature as timeSignature
+      const beatDistance: number = this.distanceFromBeat(timeSignature);
+      return Math.abs(beatDistance) < Fraction.FloatInaccuracyTolerance;
+  }
+
+  public distanceFromBeat(timeSignature: Fraction): number {
+      const beatStep: Fraction = new Fraction(1, timeSignature.Denominator);
+      const distanceFromBeat: number = this.RealValue % beatStep.RealValue; // take modulo the beat value, e.g. 1/8 in a 3/8 time signature
+      return distanceFromBeat;
+  }
+
 
   //private static equals(f1: Fraction, f2: Fraction): boolean {
   //    return f1.numerator * f2.denominator === f2.numerator * f1.denominator;
@@ -397,7 +420,7 @@ export class Fraction {
   //public static bool operator === (Fraction f1, Fraction f2)
   //{
   //    // code enhanced for performance
-  //    // System.Object.ReferenceEquals(f1, undefined) is better than if (f1 === undefined)
+  //    // System.Object.ReferenceEquals(f1, undefined) is better than if (f1)
   //    // and comparisons between booleans are quick
   //    bool f1IsNull = System.Object.ReferenceEquals(f1, undefined);
   //    bool f2IsNull = System.Object.ReferenceEquals(f2, undefined);

+ 1 - 1
src/Common/DataObjects/MusicSheetErrors.ts

@@ -8,7 +8,7 @@ export class MusicSheetErrors {
 
     public finalizeMeasure(measureNumber: number): void {
         let list: string[] = this.measureErrors[measureNumber];
-        if (list === undefined) {
+        if (!list) {
             list = [];
         }
         this.measureErrors[measureNumber] = list.concat(this.tempErrors);

+ 23 - 4
src/Common/DataObjects/Pitch.ts

@@ -73,12 +73,12 @@ export class Pitch {
      *          ret[1] = the octave shift (not the new octave!)
      * @constructor
      */
-    public static CalculateTransposedHalfTone(pitch: Pitch, transpose: number): { value: number; overflow: number; } {
+    public static CalculateTransposedHalfTone(pitch: Pitch, transpose: number): { halftone: number; overflow: number; } {
         const newHalfTone: number = <number>pitch.fundamentalNote + pitch.AccidentalHalfTones + transpose;
         return Pitch.WrapAroundCheck(newHalfTone, 12);
     }
 
-    public static WrapAroundCheck(value: number, limit: number): { value: number; overflow: number; } {
+    public static WrapAroundCheck(value: number, limit: number): { halftone: number; overflow: number; } {
         let overflow: number = 0;
 
         while (value < 0) {
@@ -89,7 +89,7 @@ export class Pitch {
             value -= limit;
             overflow++; // the octave change
         }
-        return {overflow: overflow, value: value};
+        return {overflow: overflow, halftone: value};
     }
 
     //public static calcFrequency(pitch: Pitch): number;
@@ -352,7 +352,7 @@ export class Pitch {
         // if (ReferenceEquals(p1, p2)) {
         //     return true;
         // }
-        if ((<Object>p1 === undefined) || (<Object>p2 === undefined)) {
+        if (!p1 || !p2) {
             return false;
         }
         return (p1.FundamentalNote === p2.FundamentalNote && p1.Octave === p2.Octave && p1.Accidental === p2.Accidental);
@@ -363,6 +363,25 @@ export class Pitch {
         return !(p1 === p2);
     }
 
+    //These don't take into account accidentals! which isn't needed for our current purpose
+    public OperatorFundamentalGreaterThan(p2: Pitch): boolean {
+        const p1: Pitch = this;
+        if (p1.Octave === p2.Octave) {
+            return p1.FundamentalNote > p2.FundamentalNote;
+        } else {
+            return p1.Octave > p2.Octave;
+        }
+    }
+
+    public OperatorFundamentalLessThan(p2: Pitch): boolean {
+        const p1: Pitch = this;
+        if (p1.Octave === p2.Octave) {
+            return p1.FundamentalNote < p2.FundamentalNote;
+        } else {
+            return p1.Octave < p2.Octave;
+        }
+    }
+
     // This method returns a new Pitch factor-Halftones higher than the current Pitch
     private getHigherPitchByTransposeFactor(factor: number): Pitch {
         const noteEnumIndex: number = Pitch.pitchEnumValues.indexOf(this.fundamentalNote);

+ 10 - 0
src/Common/Enums/TieTypes.ts

@@ -0,0 +1,10 @@
+/**
+ * The types of ties available
+ */
+export enum TieTypes {
+    "SIMPLE" = "",
+    "HAMMERON" = "H",
+    "PULLOFF" = "P",
+    "SLIDE" = "S",
+    "TAPPING" = "T"
+}

+ 1 - 0
src/Common/Enums/index.ts

@@ -3,3 +3,4 @@
 export * from "./FontStyles";
 export * from "./Fonts";
 export * from "./TextAlignment";
+export * from "./TieTypes";

+ 14 - 11
src/Common/FileIO/Mxl.ts

@@ -1,6 +1,5 @@
 import { IXmlElement } from "./Xml";
-import { Promise } from "es6-promise";
-import JSZip = require("jszip");
+import JSZip from "jszip";
 
 /**
  * Some helper methods to handle MXL files.
@@ -13,21 +12,25 @@ export class MXLHelper {
      * @constructor
      */
     public static MXLtoIXmlElement(data: string): Promise<IXmlElement> {
-        const zip: JSZip.JSZip = new JSZip();
+        // starting with jszip 3.4.0, JSZip.JSZip is not found,
+        //    probably because of new possibly conflicting TypeScript definitions
+        const zip: JSZip = new JSZip();
         // asynchronously load zip file and process it - with Promises
-        return zip.loadAsync(data).then(
-            (_: any) => {
-                return zip.file("META-INF/container.xml").async("string");
+        const zipLoadedAsync: Promise<JSZip> = zip.loadAsync(data);
+        const text: Promise<string> = zipLoadedAsync.then(
+            (_: JSZip) => {
+                return zip.file("META-INF/container.xml").async("text");
             },
             (err: any) => {
                 throw err;
             }
-        ).then(
+        );
+        return text.then(
             (content: string) => {
                 const parser: DOMParser = new DOMParser();
                 const doc: Document = parser.parseFromString(content, "text/xml");
                 const rootFile: string = doc.getElementsByTagName("rootfile")[0].getAttribute("full-path");
-                return zip.file(rootFile).async("string");
+                return zip.file(rootFile).async("text");
             },
             (err: any) => {
                 throw err;
@@ -53,11 +56,11 @@ export class MXLHelper {
     }
 
     public static MXLtoXMLstring(data: string): Promise<string> {
-        const zip:  JSZip.JSZip = new JSZip();
+        const zip:  JSZip = new JSZip();
         // asynchronously load zip file and process it - with Promises
         return zip.loadAsync(data).then(
             (_: any) => {
-                return zip.file("META-INF/container.xml").async("string");
+                return zip.file("META-INF/container.xml").async("text");
             },
             (err: any) => {
                 throw err;
@@ -67,7 +70,7 @@ export class MXLHelper {
                 const parser: DOMParser = new DOMParser();
                 const doc: Document = parser.parseFromString(content, "text/xml");
                 const rootFile: string = doc.getElementsByTagName("rootfile")[0].getAttribute("full-path");
-                return zip.file(rootFile).async("string");
+                return zip.file(rootFile).async("text");
             },
             (err: any) => {
                 throw err;

+ 2 - 2
src/Common/FileIO/Xml.ts

@@ -22,7 +22,7 @@ export class IXmlElement {
      * @param elem
      */
     constructor(elem: Element) {
-        if (elem === undefined) {
+        if (!elem) {
             throw new Error("IXmlElement: expected Element, got undefined");
         }
         this.elem = elem;
@@ -89,7 +89,7 @@ export class IXmlElement {
     public elements(nodeName?: string): IXmlElement[] {
         const nodes: NodeList = this.elem.childNodes;
         const ret: IXmlElement[] = [];
-        const nameUnset: boolean = nodeName === undefined;
+        const nameUnset: boolean = !nodeName;
         if (!nameUnset) {
             nodeName = nodeName.toLowerCase();
         }

+ 3 - 5
src/Common/Strings/StringUtil.ts

@@ -1,8 +1,6 @@
 export class StringUtil {
-  public static StringContainsSeparatedWord(str: string, wordRegExString: string): boolean {
-    if (new RegExp("( |^)" + wordRegExString + "([ .]|$)").test(str)) {
-      return true;
-    }
-    return false;
+  public static StringContainsSeparatedWord(str: string, wordRegExString: string, ignoreCase: boolean = false): boolean {
+    const regExp: RegExp = new RegExp("( |^)" + wordRegExString + "([ .]|$)", ignoreCase ? "i" : undefined);
+    return regExp.test(str);
   }
 }

+ 1 - 2
src/Common/index.ts

@@ -1,4 +1,3 @@
-export * from "./DataObjects/Fraction";
-export * from "./DataObjects/Pitch";
+export * from "./DataObjects";
 export * from "./Enums";
 export * from "./FileIO";

+ 1 - 1
src/MusicalScore/Exceptions.ts

@@ -5,7 +5,7 @@ export class MusicSheetReadingException implements Error {
     constructor(message: string, e?: Error) {
         //super(message);
         this.message = message;
-        if (e !== undefined) {
+        if (e) {
             this.message += " " + e.toString();
         }
     }

+ 6 - 2
src/MusicalScore/Graphical/AbstractGraphicalExpression.ts

@@ -4,6 +4,7 @@ import { StaffLine } from "./StaffLine";
 import { BoundingBox } from "./BoundingBox";
 import { AbstractExpression, PlacementEnum } from "../VoiceData/Expressions/AbstractExpression";
 import { EngravingRules } from "./EngravingRules";
+import { SourceMeasure } from "../VoiceData/SourceMeasure";
 
 export abstract class AbstractGraphicalExpression extends GraphicalObject {
     protected label: GraphicalLabel;
@@ -11,14 +12,17 @@ export abstract class AbstractGraphicalExpression extends GraphicalObject {
     /** Internal cache of read expression */
     protected expression: AbstractExpression;
     /** EngravingRules for positioning */
-    protected rules: EngravingRules = EngravingRules.Rules;
+    protected rules: EngravingRules;
+    protected parentMeasure: SourceMeasure;
 
-    constructor(parentStaffline: StaffLine, expression: AbstractExpression) {
+    constructor(parentStaffline: StaffLine, expression: AbstractExpression, measure: SourceMeasure) {
         super();
         this.expression = expression;
+        this.parentMeasure = measure; // could be undefined!
         this.boundingBox = new BoundingBox(this, parentStaffline.PositionAndShape);
         this.parentStaffLine = parentStaffline;
         this.parentStaffLine.AbstractExpressions.push(this);
+        this.rules = parentStaffline.ParentMusicSystem.rules;
     }
 
     /** Graphical label of the expression if available */

+ 23 - 6
src/MusicalScore/Graphical/AccidentalCalculator.ts

@@ -3,7 +3,8 @@ import {KeyInstruction} from "../VoiceData/Instructions/KeyInstruction";
 import {GraphicalNote} from "./GraphicalNote";
 import {Pitch} from "../../Common/DataObjects/Pitch";
 import {NoteEnum} from "../../Common/DataObjects/Pitch";
-import Dictionary from "typescript-collections/dist/lib/Dictionary";
+import { Dictionary } from "typescript-collections";
+// import { Dictionary } from "typescript-collections/dist/lib";
 import { MusicSheetCalculator } from "./MusicSheetCalculator";
 
 /**
@@ -26,6 +27,8 @@ export class AccidentalCalculator {
 
     /**
      * This method is called after each Measure
+     * It clears the in-measure alterations dict for the next measure
+     * and pre-loads with the alterations of the key signature
      */
     public doCalculationsAtEndOfMeasure(): void {
         this.currentInMeasureNoteAlterationsDict.clear();
@@ -35,7 +38,7 @@ export class AccidentalCalculator {
     }
 
     public checkAccidental(graphicalNote: GraphicalNote, pitch: Pitch): void {
-        if (pitch === undefined) {
+        if (!pitch) {
             return;
         }
         const pitchKey: number = <number>pitch.FundamentalNote + pitch.Octave * 12;
@@ -69,13 +72,25 @@ export class AccidentalCalculator {
                     this.keySignatureNoteAlterationsDict.getValue(pitchKey) !== pitch.AccidentalHalfTones) {
                     this.currentAlterationsComparedToKeyInstructionList.push(pitchKey);
                     this.currentInMeasureNoteAlterationsDict.setValue(pitchKey, pitch.AccidentalHalfTones);
-                } else {
+                } else if (pitch.Accidental !== AccidentalEnum.NONE) {
                     this.currentInMeasureNoteAlterationsDict.remove(pitchKey);
                 }
+
+                const inMeasureAlterationAccidental: AccidentalEnum = this.currentInMeasureNoteAlterationsDict.getValue(pitchKey);
+                if (pitch.Accidental === AccidentalEnum.NONE) {
+                    if (Math.abs(inMeasureAlterationAccidental) === 0.5) {
+                        // fix to remember quartersharp and quarterflat and not make them natural on following notes
+                        pitch = new Pitch(pitch.FundamentalNote, pitch.Octave, AccidentalEnum.NONE);
+                    } else {
+                        // If an AccidentalEnum.NONE is given, it would not be rendered.
+                        // We need here to convert to a AccidentalEnum.NATURAL:
+                        pitch = new Pitch(pitch.FundamentalNote, pitch.Octave, AccidentalEnum.NATURAL);
+                    }
+                }
                 MusicSheetCalculator.symbolFactory.addGraphicalAccidental(graphicalNote, pitch);
             }
-        } else {
-            if (pitch.Accidental !== AccidentalEnum.NONE && pitch.Accidental !== AccidentalEnum.NATURAL) {
+        } else { // pitchkey not in measure dict:
+            if (pitch.Accidental !== AccidentalEnum.NONE) {
                 if (!isInCurrentAlterationsToKeyList) {
                     this.currentAlterationsComparedToKeyInstructionList.push(pitchKey);
                 }
@@ -83,6 +98,8 @@ export class AccidentalCalculator {
                 MusicSheetCalculator.symbolFactory.addGraphicalAccidental(graphicalNote, pitch);
             } else {
                 if (isInCurrentAlterationsToKeyList) {
+                    // we need here a AccidentalEnum.NATURAL now to get it rendered - AccidentalEnum.NONE would not be rendered
+                    pitch = new Pitch(pitch.FundamentalNote, pitch.Octave, AccidentalEnum.NATURAL);
                     this.currentAlterationsComparedToKeyInstructionList.splice(this.currentAlterationsComparedToKeyInstructionList.indexOf(pitchKey), 1);
                     MusicSheetCalculator.symbolFactory.addGraphicalAccidental(graphicalNote, pitch);
                 }
@@ -91,7 +108,7 @@ export class AccidentalCalculator {
     }
 
     private reactOnKeyInstructionChange(): void {
-        const noteEnums: NoteEnum[] = KeyInstruction.getNoteEnumList(this.activeKeyInstruction);
+        const noteEnums: NoteEnum[] = this.activeKeyInstruction.AlteratedNotes;
         let keyAccidentalType: AccidentalEnum;
         if (this.activeKeyInstruction.Key > 0) {
             keyAccidentalType = AccidentalEnum.SHARP;

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

@@ -1,8 +1,10 @@
-import * as log from "loglevel";
+import log from "loglevel";
 import {ArgumentOutOfRangeException} from "../Exceptions";
 import {PointF2D} from "../../Common/DataObjects/PointF2D";
 import {SizeF2D} from "../../Common/DataObjects/SizeF2D";
 import {RectangleF2D} from "../../Common/DataObjects/RectangleF2D";
+import { StaffLineActivitySymbol } from "./StaffLineActivitySymbol";
+import { EngravingRules } from "./EngravingRules";
 
 /**
  * A bounding box delimits an area on the 2D plane.
@@ -46,7 +48,7 @@ export class BoundingBox {
         this.isSymbol = isSymbol;
         this.xBordersHaveBeenSet = false;
         this.yBordersHaveBeenSet = false;
-        if (parent !== undefined) {
+        if (parent) {
             this.Parent = parent;
         }
     }
@@ -201,7 +203,15 @@ export class BoundingBox {
     }
 
     public set Parent(value: BoundingBox) {
+        if (this.parent) {
+            // remove from old parent
+            const index: number = this.parent.ChildElements.indexOf(this, 0);
+            if (index > -1) {
+                this.parent.ChildElements.splice(index, 1);
+            }
+        }
         this.parent = value;
+        // add to new parent
         if (this.parent.ChildElements.indexOf(this) > -1) {
             log.error("BoundingBox of " + (this.dataObject.constructor as any).name +
             " already in children list of " + (this.parent.dataObject.constructor as any).name + "'s BoundingBox");
@@ -225,7 +235,7 @@ export class BoundingBox {
     }
 
     public setAbsolutePositionFromParent(): void {
-        if (this.parent !== undefined) {
+        if (this.parent) {
             this.absolutePosition.x = this.parent.AbsolutePosition.x + this.relativePosition.x;
             this.absolutePosition.y = this.parent.AbsolutePosition.y + this.relativePosition.y;
         } else {
@@ -240,7 +250,7 @@ export class BoundingBox {
       this.absolutePosition.x = this.relativePosition.x;
       this.absolutePosition.y = this.relativePosition.y;
       let parent: BoundingBox = this.parent;
-      while (parent !== undefined) {
+      while (parent) {
         this.absolutePosition.x += parent.relativePosition.x;
         this.absolutePosition.y += parent.relativePosition.y;
         parent = parent.parent;
@@ -367,7 +377,12 @@ export class BoundingBox {
         for (let idx: number = 0, len: number = this.ChildElements.length; idx < len; ++idx) {
             const childElement: BoundingBox = this.ChildElements[idx];
             minTop = Math.min(minTop, childElement.relativePosition.y + childElement.borderTop);
-            maxBottom = Math.max(maxBottom, childElement.relativePosition.y + childElement.borderBottom);
+            if (!EngravingRules.FixStafflineBoundingBox || !(childElement.dataObject instanceof StaffLineActivitySymbol)) {
+                maxBottom = Math.max(maxBottom, childElement.relativePosition.y + childElement.borderBottom);
+                // TODO there's a problem with the bottom bounding box of many stafflines, often caused by StaffLineActivitySymbol,
+                // often leading to the page SVG canvas being unnecessarily long in y-direction. This seems to be remedied by this workaround.
+                // see #643
+            }
             minMarginTop = Math.min(minMarginTop, childElement.relativePosition.y + childElement.borderMarginTop);
             maxMarginBottom = Math.max(maxMarginBottom, childElement.relativePosition.y + childElement.borderMarginBottom);
         }
@@ -544,13 +559,13 @@ export class BoundingBox {
 
     public getClickedObjectOfType<T>(clickPosition: PointF2D): T {
         const obj: Object = this.dataObject;
-        if (this.pointLiesInsideBorders(clickPosition) && (<T>obj !== undefined)) {
+        if (this.pointLiesInsideBorders(clickPosition) && (<T>obj)) {
             return (obj as T);
         }
         for (let idx: number = 0, len: number = this.childElements.length; idx < len; ++idx) {
             const psi: BoundingBox = this.childElements[idx];
             const innerObject: Object = psi.getClickedObjectOfType<T>(clickPosition);
-            if (innerObject !== undefined) {
+            if (innerObject) {
                 return (innerObject as T);
             }
         }
@@ -558,7 +573,7 @@ export class BoundingBox {
     }
 
     public getObjectsInRegion<T>(region: BoundingBox, liesInside: boolean = true): T[] {
-        if (<T>this.dataObject !== undefined) {
+        if (<T>this.dataObject) {
             if (liesInside) {
                 if (region.liesInsideBorders(this)) {
                     return [this.dataObject as T];

+ 26 - 2
src/MusicalScore/Graphical/DrawingEnums.ts

@@ -1,4 +1,6 @@
-import * as Collections from "typescript-collections";
+// import * as Collections from "typescript-collections";
+// import Collections = require("typescript-collections");
+import { Dictionary } from "typescript-collections";
 
 /**
  * The supported styles to draw a rectangle on the music sheet
@@ -40,7 +42,8 @@ export enum OutlineAndFillStyleEnum {
 }
 
 // tslint:disable-next-line:max-line-length A linebreak would be more confusing here
-export const OUTLINE_AND_FILL_STYLE_DICT: Collections.Dictionary<OutlineAndFillStyleEnum, string> = new Collections.Dictionary<OutlineAndFillStyleEnum, string>();
+export const OUTLINE_AND_FILL_STYLE_DICT: Dictionary<OutlineAndFillStyleEnum, string> =
+    new Dictionary<OutlineAndFillStyleEnum, string>();
 OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.BaseWritingColor, "Thistle");
 OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.FollowingCursor, "Aqua");
 OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.AlternativeFollowingCursor, "Azure");
@@ -106,3 +109,24 @@ export enum NoteState {
     Debug2,
     Debug3
 }
+
+export enum AutoColorSet {
+    /* different (boomwhacker-like) color set*/
+    C = "#d82c6b",
+    D = "#F89D15",
+    E = "#FFE21A",
+    F = "#4dbd5c",
+    G = "#009D96",
+    A = "#43469d",
+    B = "#76429c",
+    Rest = "#000000"
+
+    // color set from MuseScore Color notehead plugin version 1.1 by Werner Schweer and others
+    /*C = "#eeee00",
+    D = "#9b30ff",
+    E = "#ee9a00",
+    F = "#8b4513",
+    G = "#ff0000",
+    A = "#1e90ff",
+    B = "#00ff00"*/
+}

+ 76 - 13
src/MusicalScore/Graphical/DrawingParameters.ts

@@ -1,18 +1,27 @@
 import { EngravingRules } from "./EngravingRules";
 import { PlacementEnum } from "../VoiceData/Expressions/AbstractExpression";
 
+export enum ColoringModes {
+    XML = 0,
+    AutoColoring = 1,
+    CustomColorSet = 2
+}
+
 export enum DrawingParametersEnum {
     allon = "allon",
     compact = "compact",
+    compacttight = "compacttight",
     default = "default",
     leadsheet = "leadsheet",
     preview = "preview",
     thumbnail = "thumbnail",
 }
 
+/** Internal drawing/rendering parameters and broad modes like compact and thumbnail. Overlap with EngravingRules. */
 export class DrawingParameters {
     /** will set other settings if changed with set method */
     private drawingParametersEnum: DrawingParametersEnum;
+    private rules: EngravingRules = new EngravingRules();
     public drawHighlights: boolean;
     public drawErrors: boolean;
     public drawSelectionStartSymbol: boolean;
@@ -28,6 +37,7 @@ export class DrawingParameters {
     public drawComposer: boolean = true;
     public drawCredits: boolean = true;
     public drawPartNames: boolean = true;
+    public coloringMode: ColoringModes;
     public fingeringPosition: PlacementEnum = PlacementEnum.Left;
     /** Draw notes set to be invisible (print-object="no" in XML). */
     public drawHiddenNotes: boolean = false;
@@ -52,6 +62,9 @@ export class DrawingParameters {
             case DrawingParametersEnum.compact:
                 this.setForCompactMode();
                 break;
+            case DrawingParametersEnum.compacttight:
+                this.setForCompactTightMode();
+                break;
             case DrawingParametersEnum.default:
             default:
                 this.setForDefault();
@@ -79,10 +92,11 @@ export class DrawingParameters {
         this.drawCredits = true;
         this.DrawPartNames = true;
         this.drawHiddenNotes = true;
-        EngravingRules.Rules.CompactMode = false;
+        this.rules.CompactMode = false;
     }
 
     public setForDefault(): void {
+        this.rules.loadDefaultValues(); // this is not ideal, but it's hard to reset compactTight mode properly
         this.setForAllOn();
         this.drawHiddenNotes = false;
     }
@@ -101,16 +115,38 @@ export class DrawingParameters {
     }
 
     public setForCompactMode(): void {
-        this.setForDefault();
-        EngravingRules.Rules.CompactMode = true;
-        this.DrawTitle = false;
-        this.DrawComposer = false;
-        this.DrawLyricist = false;
+        // this.setForDefault(); // this would reset all EngravingRules to default values.
+        this.rules.CompactMode = true;
+        this.DrawCredits = false; // sets DrawComposer, DrawTitle, DrawLyricist to false
         // this.DrawPartNames = true; // unnecessary
-        this.drawCredits = false;
         this.drawHiddenNotes = false;
     }
 
+    public setForCompactTightMode(): void {
+        this.setForCompactMode(); // also sets CompactMode = true
+        this.DrawPartNames = false;
+
+        this.rules.VoiceSpacingMultiplierVexflow = 0.65;
+        this.rules.VoiceSpacingAddendVexflow = 2.0;
+
+        // 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.MinSkyBottomDistBetweenStaves = 1.0; // default 1.0. this can cause collisions with slurs and dynamics sometimes
+        this.rules.MinSkyBottomDistBetweenSystems = 1.0; // default 5.0
+        // note that this.rules === osmd.rules, since it's passed as a reference
+
+        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 = 0.0;
+        this.rules.PageLeftMargin = 2.0;
+        this.rules.PageRightMargin = 2.0;
+        // 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;
@@ -124,6 +160,22 @@ export class DrawingParameters {
     }
 
     //#region GETTER / SETTER
+    public get DrawCredits(): boolean {
+        return this.drawCredits;
+    }
+
+    public set DrawCredits(value: boolean) {
+        this.drawCredits = value;
+        this.DrawComposer = value;
+        this.DrawTitle = value;
+        this.DrawSubtitle = value;
+        this.DrawLyricist = value;
+    }
+    // TODO these drawCredits settings are duplicate in drawingParameters and EngravingRules. Maybe we only need them in EngravingRules.
+    // this sets the parameter in DrawingParameters, which in turn sets the parameter in EngravingRules.
+    // see settings below that don't call drawingParameters for the immediate approach.
+    // on the other hand, DrawingParameters has the added option of setting broad modes (e.g. compact), though they aren't that useful
+
     public get DrawTitle(): boolean {
         return this.drawTitle;
     }
@@ -131,7 +183,7 @@ export class DrawingParameters {
     /** Enable or disable drawing the Title of the piece. If disabled, will disable drawing Subtitle as well. */
     public set DrawTitle(value: boolean) {
         this.drawTitle = value;
-        EngravingRules.Rules.RenderTitle = value;
+        this.rules.RenderTitle = value;
         if (!value) { // don't draw subtitle if title isn't drawn
             this.DrawSubtitle = false;
         }
@@ -144,7 +196,7 @@ export class DrawingParameters {
     /** Enable or disable drawing the Subtitle of the piece. If enabled, will enable drawing Title as well. */
     public set DrawSubtitle(value: boolean) {
         this.drawSubtitle = value;
-        EngravingRules.Rules.RenderSubtitle = value;
+        this.rules.RenderSubtitle = value;
         if (value) {
             this.DrawTitle = true; // if subtitle is drawn, title needs to be drawn as well
         }
@@ -157,7 +209,7 @@ export class DrawingParameters {
     /** Enable or disable drawing a label for the Composer of the piece. */
     public set DrawComposer(value: boolean) {
         this.drawComposer = value;
-        EngravingRules.Rules.RenderComposer = value;
+        this.rules.RenderComposer = value;
     }
 
     public get DrawLyricist(): boolean {
@@ -166,7 +218,7 @@ export class DrawingParameters {
 
     public set DrawLyricist(value: boolean) {
         this.drawLyricist = value;
-        EngravingRules.Rules.RenderLyricist = value;
+        this.rules.RenderLyricist = value;
     }
 
     public get DrawPartNames(): boolean {
@@ -175,7 +227,10 @@ export class DrawingParameters {
 
     public set DrawPartNames(value: boolean) {
         this.drawPartNames = value;
-        EngravingRules.Rules.RenderInstrumentNames = value;
+        this.rules.RenderPartNames = value;
+        if (!this.rules.RenderPartNames) {
+            this.rules.RenderPartAbbreviations = false;
+        }
     }
 
     public get FingeringPosition(): PlacementEnum {
@@ -184,6 +239,14 @@ export class DrawingParameters {
 
     public set FingeringPosition(value: PlacementEnum) {
         this.fingeringPosition = value;
-        EngravingRules.Rules.FingeringPosition = value;
+        this.rules.FingeringPosition = value;
+    }
+
+    public get Rules(): EngravingRules {
+        return this.rules;
+    }
+
+    public set Rules(value: EngravingRules) {
+        this.rules = value;
     }
 }

+ 590 - 1396
src/MusicalScore/Graphical/EngravingRules.ts

@@ -1,417 +1,559 @@
 import { PagePlacementEnum } from "./GraphicalMusicPage";
 //import {MusicSymbol} from "./MusicSymbol";
-import * as log from "loglevel";
+import log from "loglevel";
 import { TextAlignmentEnum } from "../../Common/Enums/TextAlignment";
 import { PlacementEnum } from "../VoiceData/Expressions/AbstractExpression";
-import { AutoBeamOptions } from "../../OpenSheetMusicDisplay/OSMDOptions";
+import { AutoBeamOptions, AlignRestOption, FillEmptyMeasuresWithWholeRests } from "../../OpenSheetMusicDisplay/OSMDOptions";
+import { ColoringModes as ColoringMode } from "./DrawingParameters";
+import { Dictionary } from "typescript-collections";
+import { FontStyles } from "../../Common/Enums";
+import { NoteEnum } from "../../Common/DataObjects/Pitch";
+import { ChordSymbolEnum } from "../../MusicalScore/VoiceData/ChordSymbolContainer";
 
 export class EngravingRules {
-    private static rules: EngravingRules;
     /** A unit of distance. 1.0 is the distance between lines of a stave for OSMD, which is 10 pixels in Vexflow. */
-    private static unit: number = 1.0;
-    private samplingUnit: number;
-    private staccatoShorteningFactor: number;
+    public static unit: number = 1.0;
+    public SamplingUnit: number;
+    public StaccatoShorteningFactor: number;
     /** Height (size) of the sheet title. */
-    private sheetTitleHeight: number;
-    private sheetSubtitleHeight: number;
-    private sheetMinimumDistanceBetweenTitleAndSubtitle: number;
-    private sheetComposerHeight: number;
-    private sheetAuthorHeight: number;
-    private compactMode: boolean;
-    private pagePlacementEnum: PagePlacementEnum;
-    private pageHeight: number;
-    private pageTopMargin: number;
-    private pageTopMarginNarrow: number;
-    private pageBottomMargin: number;
-    private pageLeftMargin: number;
-    private pageRightMargin: number;
-    private titleTopDistance: number;
-    private titleBottomDistance: number;
-    private systemDistance: number;
-    private systemLeftMargin: number;
-    private systemRightMargin: number;
-    private firstSystemMargin: number;
-    private systemLabelsRightMargin: number;
-    private systemComposerDistance: number;
-    private instrumentLabelTextHeight: number;
-    private minimumAllowedDistanceBetweenSystems: number;
-    private lastSystemMaxScalingFactor: number;
-    private staffDistance: number;
-    private betweenStaffDistance: number;
-    private staffHeight: number;
-    private betweenStaffLinesDistance: number;
+    public SheetTitleHeight: number;
+    public SheetSubtitleHeight: number;
+    public SheetMinimumDistanceBetweenTitleAndSubtitle: number;
+    public SheetComposerHeight: number;
+    public SheetAuthorHeight: number;
+    public CompactMode: boolean;
+    public PagePlacementEnum: PagePlacementEnum;
+    public PageHeight: number;
+    public PageTopMargin: number;
+    public PageTopMarginNarrow: number;
+    public PageBottomMargin: number;
+    public PageLeftMargin: number;
+    public PageRightMargin: number;
+    public TitleTopDistance: number;
+    public TitleBottomDistance: number;
+    public SystemLeftMargin: number;
+    public SystemRightMargin: number;
+    public SystemLabelsRightMargin: number;
+    public SystemComposerDistance: number;
+    public InstrumentLabelTextHeight: number;
+    public MinimumDistanceBetweenSystems: number;
+    public MinSkyBottomDistBetweenSystems: number;
+    public LastSystemMaxScalingFactor: number;
+    public StaffDistance: number;
+    public BetweenStaffDistance: number;
+    public StaffHeight: number;
+    public TabStaffInterlineHeight: number;
+    public BetweenStaffLinesDistance: number;
     /** Whether to automatically beam notes that don't already have beams in XML. */
-    private autoBeamNotes: boolean;
+    public AutoBeamNotes: boolean;
     /** Options for autoBeaming like whether to beam over rests. See AutoBeamOptions interface. */
-    private autoBeamOptions: AutoBeamOptions;
-    private beamWidth: number;
-    private beamSpaceWidth: number;
-    private beamForwardLength: number;
-    private clefLeftMargin: number;
-    private clefRightMargin: number;
-    private betweenKeySymbolsDistance: number;
-    private keyRightMargin: number;
-    private rhythmRightMargin: number;
-    private inStaffClefScalingFactor: number;
-    private distanceBetweenNaturalAndSymbolWhenCancelling: number;
-    private noteHelperLinesOffset: number;
-    private measureLeftMargin: number;
-    private measureRightMargin: number;
-    private distanceBetweenLastInstructionAndRepetitionBarline: number;
-    private arpeggioDistance: number;
-    private idealStemLength: number;
-    private stemNoteHeadBorderYOffset: number;
-    private stemWidth: number;
-    private stemMargin: number;
-    private stemMinLength: number;
-    private stemMaxLength: number;
-    private beamSlopeMaxAngle: number;
-    private stemMinAllowedDistanceBetweenNoteHeadAndBeamLine: number;
-    private setWantedStemDirectionByXml: boolean;
-    private graceNoteScalingFactor: number;
-    private graceNoteXOffset: number;
-    private wedgeOpeningLength: number;
-    private wedgeMeasureEndOpeningLength: number;
-    private wedgeMeasureBeginOpeningLength: number;
-    private wedgePlacementAboveY: number;
-    private wedgePlacementBelowY: number;
-    private wedgeHorizontalMargin: number;
-    private wedgeVerticalMargin: number;
-    private distanceOffsetBetweenTwoHorizontallyCrossedWedges: number;
-    private wedgeMinLength: number;
-    private distanceBetweenAdjacentDynamics: number;
-    private tempoChangeMeasureValidity: number;
-    private tempoContinousFactor: number;
-    private staccatoScalingFactor: number;
-    private betweenDotsDistance: number;
-    private ornamentAccidentalScalingFactor: number;
-    private chordSymbolTextHeight: number;
-    private chordSymbolYOffset: number;
-    private fingeringLabelFontHeight: number;
-    private measureNumberLabelHeight: number;
-    private measureNumberLabelOffset: number;
+    public AutoBeamOptions: AutoBeamOptions;
+    public BeamWidth: number;
+    public BeamSpaceWidth: number;
+    public BeamForwardLength: number;
+    public FlatBeams: boolean;
+    public FlatBeamOffset: number;
+    public FlatBeamOffsetPerBeam: number;
+    public ClefLeftMargin: number;
+    public ClefRightMargin: number;
+    public PercussionOneLineCutoff: number;
+    public PercussionForceVoicesOneLineCutoff: number;
+    public BetweenKeySymbolsDistance: number;
+    public KeyRightMargin: number;
+    public RhythmRightMargin: number;
+    public ShowRhythmAgainAfterPartEndOrFinalBarline: boolean;
+    public NewPartAndSystemAfterFinalBarline: boolean;
+    public InStaffClefScalingFactor: number;
+    public DistanceBetweenNaturalAndSymbolWhenCancelling: number;
+    public NoteHelperLinesOffset: number;
+    public MeasureLeftMargin: number;
+    public MeasureRightMargin: number;
+    public DistanceBetweenLastInstructionAndRepetitionBarline: number;
+    public ArpeggioDistance: number;
+    public IdealStemLength: number;
+    public StemNoteHeadBorderYOffset: number;
+    public StemWidth: number;
+    public StemMargin: number;
+    public StemMinLength: number;
+    public StemMaxLength: number;
+    public BeamSlopeMaxAngle: number;
+    public StemMinAllowedDistanceBetweenNoteHeadAndBeamLine: number;
+    public SetWantedStemDirectionByXml: boolean;
+    public GraceNoteScalingFactor: number;
+    public GraceNoteXOffset: number;
+    public WedgeOpeningLength: number;
+    public WedgeMeasureEndOpeningLength: number;
+    public WedgeMeasureBeginOpeningLength: number;
+    public WedgePlacementAboveY: number;
+    public WedgePlacementBelowY: number;
+    public WedgeHorizontalMargin: number;
+    public WedgeVerticalMargin: number;
+    public DistanceOffsetBetweenTwoHorizontallyCrossedWedges: number;
+    public WedgeMinLength: number;
+    public DistanceBetweenAdjacentDynamics: number;
+    public TempoChangeMeasureValidity: number;
+    public TempoContinousFactor: number;
+    public StaccatoScalingFactor: number;
+    public BetweenDotsDistance: number;
+    public OrnamentAccidentalScalingFactor: number;
+    public ChordSymbolTextHeight: number;
+    public ChordSymbolXSpacing: number;
+    public ChordSymbolYOffset: number;
+    public ChordSymbolLabelTexts: Dictionary<ChordSymbolEnum, string>;
+    public RepetitionSymbolsYOffset: number;
+    public MeasureNumberLabelHeight: number;
+    public MeasureNumberLabelOffset: number;
+    public MeasureNumberLabelXOffset: number;
     /** Whether tuplets should display ratio (3:2 instead of 3 for triplet). Default false. */
-    private tupletsRatioed: boolean;
+    public TupletsRatioed: boolean;
     /** Whether all tuplets should be bracketed (e.g. |--5--| instead of 5). Default false.
      * If false, only tuplets given as bracketed in XML (bracket="yes") will be bracketed.
      * (If not given in XML, bracketing is implementation-dependent according to standard)
      */
-    private tupletsBracketed: boolean;
+    public TupletsBracketed: boolean;
     /** Whether all triplets should be bracketed. Overrides tupletsBracketed for triplets.
      * If false, only triplets given as bracketed in XML (bracket="yes") will be bracketed.
      * (Bracketing all triplets can be cluttering)
      */
-    private tripletsBracketed: boolean;
-    private tupletNumberLabelHeight: number;
-    private tupletNumberYOffset: number;
-    private labelMarginBorderFactor: number;
-    private tupletVerticalLineLength: number;
-    private repetitionEndingLabelHeight: number;
-    private repetitionEndingLabelXOffset: number;
-    private repetitionEndingLabelYOffset: number;
-    private repetitionEndingLineYLowerOffset: number;
-    private repetitionEndingLineYUpperOffset: number;
+    public TripletsBracketed: boolean;
+    public TupletNumberLabelHeight: number;
+    public TupletNumberYOffset: number;
+    public LabelMarginBorderFactor: number;
+    public TupletVerticalLineLength: number;
+    public TupletNumbersInTabs: boolean;
+
+    public RepetitionEndingLabelHeight: number;
+    public RepetitionEndingLabelXOffset: number;
+    public RepetitionEndingLabelYOffset: number;
+    public RepetitionEndingLineYLowerOffset: number;
+    public RepetitionEndingLineYUpperOffset: number;
+    public VoltaOffset: number;
     /** Default alignment of lyrics.
      * Left alignments will extend text to the right of the bounding box,
      * which facilitates spacing by extending measure width.
      */
-    private lyricsAlignmentStandard: TextAlignmentEnum;
-    private lyricsHeight: number;
-    private lyricsYOffsetToStaffHeight: number;
-    private verticalBetweenLyricsDistance: number;
-    private horizontalBetweenLyricsDistance: number;
-    private betweenSyllableMaximumDistance: number;
-    private betweenSyllableMinimumDistance: number;
-    private lyricOverlapAllowedIntoNextMeasure: number;
-    private minimumDistanceBetweenDashes: number;
-    private bezierCurveStepSize: number;
-    private tPower3: number[];
-    private oneMinusTPower3: number[];
-    private factorOne: number[];
-    private factorTwo: number[];
-    private tieGhostObjectWidth: number;
-    private tieYPositionOffsetFactor: number;
-    private minimumNeededXspaceForTieGhostObject: number;
-    private tieHeightMinimum: number;
-    private tieHeightMaximum: number;
-    private tieHeightInterpolationK: number;
-    private tieHeightInterpolationD: number;
-    private slurNoteHeadYOffset: number;
-    private slurStemXOffset: number;
-    private slurSlopeMaxAngle: number;
-    private slurTangentMinAngle: number;
-    private slurTangentMaxAngle: number;
-    private slursStartingAtSameStaffEntryYOffset: number;
-    private instantaneousTempoTextHeight: number;
-    private continuousDynamicTextHeight: number;
-    private moodTextHeight: number;
-    private unknownTextHeight: number;
-    private continuousTempoTextHeight: number;
-    private staffLineWidth: number;
-    private ledgerLineWidth: number;
-    private wedgeLineWidth: number;
-    private tupletLineWidth: number;
-    private lyricUnderscoreLineWidth: number;
-    private systemThinLineWidth: number;
-    private systemBoldLineWidth: number;
-    private systemRepetitionEndingLineWidth: number;
-    private systemDotWidth: number;
-    private distanceBetweenVerticalSystemLines: number;
-    private distanceBetweenDotAndLine: number;
-    private octaveShiftLineWidth: number;
-    private octaveShiftVerticalLineLength: number;
-    private graceLineWidth: number;
-    private minimumStaffLineDistance: number;
-    private minimumCrossedBeamDifferenceMargin: number;
-    private displacedNoteMargin: number;
-    private minNoteDistance: number;
-    private subMeasureXSpacingThreshold: number;
-    private measureDynamicsMaxScalingFactor: number;
-    private wholeRestXShiftVexflow: number;
-    private metronomeMarkXShift: number;
-    private metronomeMarkYShift: 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];
-    private durationDistanceDict: {[_: number]: number; } = {};
-    private durationScalingDistanceDict: {[_: number]: number; } = {};
+    public LyricsAlignmentStandard: TextAlignmentEnum;
+    public LyricsHeight: number;
+    public LyricsYOffsetToStaffHeight: number;
+    public VerticalBetweenLyricsDistance: number;
+    public HorizontalBetweenLyricsDistance: number;
+    public BetweenSyllableMaximumDistance: number;
+    public BetweenSyllableMinimumDistance: number;
+    public LyricOverlapAllowedIntoNextMeasure: number;
+    public MinimumDistanceBetweenDashes: number;
+    public MaximumLyricsElongationFactor: number;
+
+    public SlurPlacementFromXML: boolean;
+    public BezierCurveStepSize: number;
+    public TPower3: number[];
+    public OneMinusTPower3: number[];
+    public FactorOne: number[];
+    public FactorTwo: number[];
+    public TieGhostObjectWidth: number;
+    public TieYPositionOffsetFactor: number;
+    public MinimumNeededXspaceForTieGhostObject: number;
+    public TieHeightMinimum: number;
+    public TieHeightMaximum: number;
+    public TieHeightInterpolationK: number;
+    public TieHeightInterpolationD: number;
+    public SlurNoteHeadYOffset: number;
+    public SlurStemXOffset: number;
+    public SlurSlopeMaxAngle: number;
+    public SlurTangentMinAngle: number;
+    public SlurTangentMaxAngle: number;
+    public SlursStartingAtSameStaffEntryYOffset: number;
+    public InstantaneousTempoTextHeight: number;
+    public ContinuousDynamicTextHeight: number;
+    public MoodTextHeight: number;
+    public UnknownTextHeight: number;
+    public ContinuousTempoTextHeight: number;
+    public VexFlowDefaultNotationFontScale: number;
+    public VexFlowDefaultTabFontScale: number;
+    public TremoloStrokeScale: number;
+    public TremoloYSpacingScale: number;
+    public StaffLineWidth: number;
+    public StaffLineColor: string;
+    public LedgerLineWidth: number;
+    public LedgerLineStrokeStyle: string;
+    public LedgerLineColorDefault: string;
+    public WedgeLineWidth: number;
+    public TupletLineWidth: number;
+    public LyricUnderscoreLineWidth: number;
+    public SystemThinLineWidth: number;
+    public SystemBoldLineWidth: number;
+    public SystemRepetitionEndingLineWidth: number;
+    public SystemDotWidth: number;
+    public MultipleRestMeasureDefaultWidth: number;
+    public DistanceBetweenVerticalSystemLines: number;
+    public DistanceBetweenDotAndLine: number;
+    public OctaveShiftLineWidth: number;
+    public OctaveShiftVerticalLineLength: number;
+    public GraceLineWidth: number;
+    public MinimumStaffLineDistance: number;
+    public MinSkyBottomDistBetweenStaves: number;
+    public MinimumCrossedBeamDifferenceMargin: number;
+
+    public VoiceSpacingMultiplierVexflow: number;
+    public VoiceSpacingAddendVexflow: number;
+    public DisplacedNoteMargin: number;
+    public MinNoteDistance: number;
+    public SubMeasureXSpacingThreshold: number;
+    public MeasureDynamicsMaxScalingFactor: number;
+    public WholeRestXShiftVexflow: number;
+    public MetronomeMarksDrawn: boolean;
+    public MetronomeMarkXShift: number;
+    public MetronomeMarkYShift: number;
+    public SoftmaxFactorVexFlow: number;
+    public MaxInstructionsConstValue: number;
+    public NoteDistances: number[] = [1.0, 1.0, 1.3, 1.6, 2.0, 2.5, 3.0, 4.0];
+    public NoteDistancesScalingFactors: number[] = [1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0];
+    public DurationDistanceDict: {[_: number]: number; } = {};
+    public DurationScalingDistanceDict: {[_: number]: number; } = {};
+
+    public AlignRests: number; // 0 = false, 1 = true, 2 = auto
+    public FillEmptyMeasuresWithWholeRest: FillEmptyMeasuresWithWholeRests | number;
+    public ArpeggiosGoAcrossVoices: boolean;
+    public RenderArpeggios: boolean;
+    public RenderSlurs: boolean;
+    public ColoringMode: ColoringMode;
+    public ColoringEnabled: boolean;
+    public ColorStemsLikeNoteheads: boolean;
+    public ColorFlags: boolean;
+    public ColorBeams: boolean;
+    public ColoringSetCurrent: Dictionary<NoteEnum|number, string>;
+    public DefaultColorNotehead: string;
+    public DefaultColorRest: string;
+    public DefaultColorStem: string;
+    public DefaultColorLabel: string;
+    public DefaultColorTitle: string;
+    public DefaultFontFamily: string;
+    public DefaultFontStyle: FontStyles;
+    public DefaultVexFlowNoteFont: string;
+    public MaxMeasureToDrawIndex: number;
+    public MinMeasureToDrawIndex: number;
+    public MaxPageToDrawNumber: number;
+    public MaxSystemToDrawNumber: number;
 
-    private coloringEnabled: boolean;
-    private colorFlags: boolean;
-    private colorBeams: boolean;
-    private defaultColorNotehead: string;
-    private defaultColorRest: string;
-    private defaultColorStem: string;
-    private defaultColorLabel: string;
-    private defaultColorTitle: string;
-    private maxMeasureToDrawIndex: number;
     /** Whether to render a label for the composer of the piece at the top of the sheet. */
-    private renderComposer: boolean;
-    private renderTitle: boolean;
-    private renderSubtitle: boolean;
-    private renderLyricist: boolean;
-    private renderInstrumentNames: boolean;
-    private renderFingerings: boolean;
-    private dynamicExpressionMaxDistance: number;
-    private dynamicExpressionSpacer: number;
+    public RenderComposer: boolean;
+    public RenderTitle: boolean;
+    public RenderSubtitle: boolean;
+    public RenderLyricist: boolean;
+    public RenderPartNames: boolean;
+    public RenderPartAbbreviations: boolean;
+    public RenderFingerings: boolean;
+    public RenderMeasureNumbers: boolean;
+    public RenderMeasureNumbersOnlyAtSystemStart: boolean;
+    public UseXMLMeasureNumbers: boolean;
+    public RenderLyrics: boolean;
+    public RenderMultipleRestMeasures: boolean;
+    public AutoGenerateMutipleRestMeasuresFromRestMeasures: boolean;
+    public RenderTimeSignatures: boolean;
+    public DynamicExpressionMaxDistance: number;
+    public DynamicExpressionSpacer: number;
+    public ArticulationPlacementFromXML: boolean;
     /** Position of fingering label in relation to corresponding note (left, right supported, above, below experimental) */
-    private fingeringPosition: PlacementEnum;
-    private fingeringInsideStafflines: boolean;
+    public FingeringPosition: PlacementEnum;
+    public FingeringInsideStafflines: boolean;
+    public FingeringLabelFontHeight: number;
+    public FingeringOffsetX: number;
+    public NewSystemAtXMLNewSystemAttribute: boolean;
+    public NewPageAtXMLNewPageAttribute: boolean;
+    public PageFormat: PageFormat;
+    public PageBackgroundColor: string; // vexflow-color-string (#FFFFFF). Default undefined/transparent.
+    public RenderSingleHorizontalStaffline: boolean;
+    public RestoreCursorAfterRerender: boolean;
+    public StretchLastSystemLine: boolean;
+    public SpacingBetweenTextLines: number;
+
+    public static FixStafflineBoundingBox: boolean; // TODO temporary workaround
 
     constructor() {
+        this.loadDefaultValues();
+    }
+
+    public loadDefaultValues(): void {
         // global variables
-        this.samplingUnit = EngravingRules.unit * 3;
+        this.SamplingUnit = EngravingRules.unit * 3;
 
         // Page Label Variables
-        this.sheetTitleHeight = 4.0;
-        this.sheetSubtitleHeight = 2.0;
-        this.sheetMinimumDistanceBetweenTitleAndSubtitle = 1.0;
-        this.sheetComposerHeight = 2.0;
-        this.sheetAuthorHeight = 2.0;
+        this.SheetTitleHeight = 4.0;
+        this.SheetSubtitleHeight = 2.0;
+        this.SheetMinimumDistanceBetweenTitleAndSubtitle = 1.0;
+        this.SheetComposerHeight = 2.0;
+        this.SheetAuthorHeight = 2.0;
 
         // Staff sizing Variables
-        this.compactMode = false;
-        this.pagePlacementEnum = PagePlacementEnum.Down;
-        this.pageHeight = 100001.0;
-        this.pageTopMargin = 5.0;
-        this.pageTopMarginNarrow = 0.0; // for compact mode
-        this.pageBottomMargin = 5.0;
-        this.pageLeftMargin = 5.0;
-        this.pageRightMargin = 5.0;
-        this.titleTopDistance = 9.0;
-        this.titleBottomDistance = 1.0;
-        this.staffDistance = 7.0;
-        this.betweenStaffDistance = 5.0;
+        this.CompactMode = false;
+        this.PagePlacementEnum = PagePlacementEnum.Down;
+        this.PageHeight = 100001.0;
+        this.PageTopMargin = 5.0;
+        this.PageTopMarginNarrow = 0.0; // for compact mode
+        this.PageBottomMargin = 5.0;
+        this.PageLeftMargin = 5.0;
+        this.PageRightMargin = 5.0;
+        this.TitleTopDistance = 5.0;
+        this.TitleBottomDistance = 1.0;
+        this.StaffDistance = 7.0;
+        this.BetweenStaffDistance = 5.0;
+        this.MinimumStaffLineDistance = 4.0;
+        this.MinSkyBottomDistBetweenStaves = 1.0; // default. compacttight mode sets it to 1.0 (as well).
 
         // System Sizing and Label Variables
-        this.staffHeight = 4.0;
-        this.betweenStaffLinesDistance = EngravingRules.unit;
-        this.systemDistance = 10.0;
-        this.systemLeftMargin = 0.0;
-        this.systemRightMargin = 0.0;
-        this.firstSystemMargin = 15.0;
-        this.systemLabelsRightMargin = 2.0;
-        this.systemComposerDistance = 2.0;
-        this.instrumentLabelTextHeight = 2;
-        this.minimumAllowedDistanceBetweenSystems = 3.0;
-        this.lastSystemMaxScalingFactor = 1.4;
+        this.StaffHeight = 4.0;
+        this.TabStaffInterlineHeight = 1.1111;
+        this.BetweenStaffLinesDistance = EngravingRules.unit;
+        this.SystemLeftMargin = 0.0;
+        this.SystemRightMargin = 0.0;
+        this.SystemLabelsRightMargin = 2.0;
+        this.SystemComposerDistance = 2.0;
+        this.InstrumentLabelTextHeight = 2;
+        this.MinimumDistanceBetweenSystems = 7.0;
+        this.MinSkyBottomDistBetweenSystems = 5.0;
+        this.LastSystemMaxScalingFactor = 1.4;
 
         // autoBeam options
-        this.autoBeamNotes = false;
-        this.autoBeamOptions = {
+        this.AutoBeamNotes = false;
+        this.AutoBeamOptions = {
             beam_middle_rests_only: false,
             beam_rests: false,
             maintain_stem_directions: false
         };
 
         // Beam Sizing Variables
-        this.beamWidth = EngravingRules.unit / 2.0;
-        this.beamSpaceWidth = EngravingRules.unit / 3.0;
-        this.beamForwardLength = 1.25 * EngravingRules.unit;
+        this.BeamWidth = EngravingRules.unit / 2.0;
+        this.BeamSpaceWidth = EngravingRules.unit / 3.0;
+        this.BeamForwardLength = 1.25 * EngravingRules.unit;
+
+        this.FlatBeams = false;
+        this.FlatBeamOffset = 20;
+        this.FlatBeamOffsetPerBeam = 10;
 
         // Beam Sizing Variables
-        this.clefLeftMargin = 0.5;
-        this.clefRightMargin = 0.75;
-        this.betweenKeySymbolsDistance = 0.2;
-        this.keyRightMargin = 0.75;
-        this.rhythmRightMargin = 1.25;
-        this.inStaffClefScalingFactor = 0.8;
-        this.distanceBetweenNaturalAndSymbolWhenCancelling = 0.4;
+        this.ClefLeftMargin = 0.5;
+        this.ClefRightMargin = 0.75;
+        this.PercussionOneLineCutoff = 3;
+        this.PercussionForceVoicesOneLineCutoff = 1;
+        this.BetweenKeySymbolsDistance = 0.2;
+        this.KeyRightMargin = 0.75;
+        this.RhythmRightMargin = 1.25;
+        this.ShowRhythmAgainAfterPartEndOrFinalBarline = true;
+        this.NewPartAndSystemAfterFinalBarline = false;
+        this.InStaffClefScalingFactor = 0.8;
+        this.DistanceBetweenNaturalAndSymbolWhenCancelling = 0.4;
 
         // Beam Sizing Variables
-        this.noteHelperLinesOffset = 0.25;
-        this.measureLeftMargin = 0.7;
-        this.measureRightMargin = 0.0;
-        this.distanceBetweenLastInstructionAndRepetitionBarline = 1.0;
-        this.arpeggioDistance = 0.6;
+        this.NoteHelperLinesOffset = 0.25;
+        this.MeasureLeftMargin = 0.7;
+        this.MeasureRightMargin = 0.0;
+        this.DistanceBetweenLastInstructionAndRepetitionBarline = 1.0;
+        this.ArpeggioDistance = 0.6;
 
         // Stems Variables
-        this.staccatoShorteningFactor = 2;
-        this.idealStemLength = 3.0;
-        this.stemNoteHeadBorderYOffset = 0.2;
-        this.stemWidth = 0.13;
-        this.stemMargin = 0.2;
-        this.stemMinLength = 2.5;
-        this.stemMaxLength = 4.5;
-        this.beamSlopeMaxAngle = 10.0;
-        this.stemMinAllowedDistanceBetweenNoteHeadAndBeamLine = 1.0;
-        this.setWantedStemDirectionByXml = true;
+        this.StaccatoShorteningFactor = 2;
+        this.IdealStemLength = 3.0;
+        this.StemNoteHeadBorderYOffset = 0.2;
+        this.StemMargin = 0.2;
+        this.StemMinLength = 2.5;
+        this.StemMaxLength = 4.5;
+        this.BeamSlopeMaxAngle = 10.0;
+        this.StemMinAllowedDistanceBetweenNoteHeadAndBeamLine = 1.0;
+        this.SetWantedStemDirectionByXml = true;
+        // also see stemwidth further below
 
         // GraceNote Variables
-        this.graceNoteScalingFactor = 0.6;
-        this.graceNoteXOffset = 0.2;
+        this.GraceNoteScalingFactor = 0.6;
+        this.GraceNoteXOffset = 0.2;
 
         // Wedge Variables
-        this.wedgeOpeningLength = 1.2;
-        this.wedgeMeasureEndOpeningLength = 0.75;
-        this.wedgeMeasureBeginOpeningLength = 0.75;
-        this.wedgePlacementAboveY = -1.5;
-        this.wedgePlacementBelowY = 1.5;
-        this.wedgeHorizontalMargin = 0.6;
-        this.wedgeVerticalMargin = 0.5;
-        this.distanceOffsetBetweenTwoHorizontallyCrossedWedges = 0.3;
-        this.wedgeMinLength = 2.0;
-        this.distanceBetweenAdjacentDynamics = 0.75;
+        this.WedgeOpeningLength = 1.2;
+        this.WedgeMeasureEndOpeningLength = 0.75;
+        this.WedgeMeasureBeginOpeningLength = 0.75;
+        this.WedgePlacementAboveY = -1.5;
+        this.WedgePlacementBelowY = 1.5;
+        this.WedgeHorizontalMargin = 0.6;
+        this.WedgeVerticalMargin = 0.5;
+        this.DistanceOffsetBetweenTwoHorizontallyCrossedWedges = 0.3;
+        this.WedgeMinLength = 2.0;
+        this.DistanceBetweenAdjacentDynamics = 0.75;
 
         // Tempo Variables
-        this.tempoChangeMeasureValidity = 4;
-        this.tempoContinousFactor = 0.7;
+        this.TempoChangeMeasureValidity = 4;
+        this.TempoContinousFactor = 0.7;
 
         // various
-        this.staccatoScalingFactor = 0.8;
-        this.betweenDotsDistance = 0.8;
-        this.ornamentAccidentalScalingFactor = 0.65;
-        this.chordSymbolTextHeight = 2.0;
-        this.chordSymbolYOffset = 2.0;
-        this.fingeringLabelFontHeight = 1.7;
+        this.StaccatoScalingFactor = 0.8;
+        this.BetweenDotsDistance = 0.8;
+        this.OrnamentAccidentalScalingFactor = 0.65;
+        this.ChordSymbolTextHeight = 2.0;
+        this.ChordSymbolXSpacing = 1.0;
+        this.ChordSymbolYOffset = 2.0;
+        this.ChordSymbolLabelTexts = new Dictionary<ChordSymbolEnum, string>();
+        this.resetChordSymbolLabelTexts(this.ChordSymbolLabelTexts);
+        this.RepetitionSymbolsYOffset = 0;
 
         // Tuplets, MeasureNumber and TupletNumber Labels
-        this.measureNumberLabelHeight = 1.5 * EngravingRules.unit;
-        this.measureNumberLabelOffset = 2;
-        this.tupletsRatioed = false;
-        this.tupletsBracketed = false;
-        this.tripletsBracketed = false; // special setting for triplets, overrides tuplet setting (for triplets only)
-        this.tupletNumberLabelHeight = 1.5 * EngravingRules.unit;
-        this.tupletNumberYOffset = 0.5;
-        this.labelMarginBorderFactor = 0.1;
-        this.tupletVerticalLineLength = 0.5;
+        this.MeasureNumberLabelHeight = 1.5 * EngravingRules.unit;
+        this.MeasureNumberLabelOffset = 2;
+        this.MeasureNumberLabelXOffset = -0.5;
+        this.TupletsRatioed = false;
+        this.TupletsBracketed = false;
+        this.TripletsBracketed = false; // special setting for triplets, overrides tuplet setting (for triplets only)
+        this.TupletNumberLabelHeight = 1.5 * EngravingRules.unit;
+        this.TupletNumberYOffset = 0.5;
+        this.LabelMarginBorderFactor = 0.1;
+        this.TupletVerticalLineLength = 0.5;
+        this.TupletNumbersInTabs = false; // disabled by default, nonstandard in tabs, at least how we show them in non-tabs.
 
         // Slur and Tie variables
-        this.bezierCurveStepSize = 1000;
+        this.SlurPlacementFromXML = true;
+        this.BezierCurveStepSize = 1000;
         this.calculateCurveParametersArrays();
-        this.tieGhostObjectWidth = 0.75;
-        this.tieYPositionOffsetFactor = 0.3;
-        this.minimumNeededXspaceForTieGhostObject = 1.0;
-        this.tieHeightMinimum = 0.28;
-        this.tieHeightMaximum = 1.2;
-        this.tieHeightInterpolationK = 0.0288;
-        this.tieHeightInterpolationD = 0.136;
-        this.slurNoteHeadYOffset = 0.5;
-        this.slurStemXOffset = 0.3;
-        this.slurSlopeMaxAngle = 15.0;
-        this.slurTangentMinAngle = 30.0;
-        this.slurTangentMaxAngle = 80.0;
-        this.slursStartingAtSameStaffEntryYOffset = 0.8;
+        this.TieGhostObjectWidth = 0.75;
+        this.TieYPositionOffsetFactor = 0.3;
+        this.MinimumNeededXspaceForTieGhostObject = 1.0;
+        this.TieHeightMinimum = 0.28;
+        this.TieHeightMaximum = 1.2;
+        this.TieHeightInterpolationK = 0.0288;
+        this.TieHeightInterpolationD = 0.136;
+        this.SlurNoteHeadYOffset = 0.5;
+        this.SlurStemXOffset = 0.3;
+        this.SlurSlopeMaxAngle = 15.0;
+        this.SlurTangentMinAngle = 30.0;
+        this.SlurTangentMaxAngle = 80.0;
+        this.SlursStartingAtSameStaffEntryYOffset = 0.8;
 
         // Repetitions
-        this.repetitionEndingLabelHeight = 2.0;
-        this.repetitionEndingLabelXOffset = 0.5;
-        this.repetitionEndingLabelYOffset = 0.3;
-        this.repetitionEndingLineYLowerOffset = 0.5;
-        this.repetitionEndingLineYUpperOffset = 0.3;
+        this.RepetitionEndingLabelHeight = 2.0;
+        this.RepetitionEndingLabelXOffset = 0.5;
+        this.RepetitionEndingLabelYOffset = 0.3;
+        this.RepetitionEndingLineYLowerOffset = 0.5;
+        this.RepetitionEndingLineYUpperOffset = 0.3;
+        this.VoltaOffset = 2.5;
 
         // Lyrics
-        this.lyricsAlignmentStandard = TextAlignmentEnum.LeftBottom; // CenterBottom and LeftBottom tested, spacing-optimized
-        this.lyricsHeight = 2.0; // actually size of lyrics
-        this.lyricsYOffsetToStaffHeight = 3.0; // distance between lyrics and staff. could partly be even lower/dynamic
-        this.verticalBetweenLyricsDistance = 0.5;
-        this.horizontalBetweenLyricsDistance = 0.2;
-        this.betweenSyllableMaximumDistance = 10.0;
-        this.betweenSyllableMinimumDistance = 0.5; // + 1.0 for CenterAlignment added in lyrics spacing
-        this.lyricOverlapAllowedIntoNextMeasure = 3.4; // optimal for dashed last lyric, see Land der Berge
-        this.minimumDistanceBetweenDashes = 10;
+        this.LyricsAlignmentStandard = TextAlignmentEnum.LeftBottom; // CenterBottom and LeftBottom tested, spacing-optimized
+        this.LyricsHeight = 2.0; // actually size of lyrics
+        this.LyricsYOffsetToStaffHeight = 0.0; // distance between lyrics and staff. could partly be even lower/dynamic
+        this.VerticalBetweenLyricsDistance = 0.5;
+        this.HorizontalBetweenLyricsDistance = 0.2;
+        this.BetweenSyllableMaximumDistance = 10.0;
+        this.BetweenSyllableMinimumDistance = 0.5; // + 1.0 for CenterAlignment added in lyrics spacing
+        this.LyricOverlapAllowedIntoNextMeasure = 3.4; // optimal for dashed last lyric, see Land der Berge
+        this.MinimumDistanceBetweenDashes = 10;
+        this.MaximumLyricsElongationFactor = 2.5;
 
         // expressions variables
-        this.instantaneousTempoTextHeight = 2.3;
-        this.continuousDynamicTextHeight = 2.3;
-        this.moodTextHeight = 2.3;
-        this.unknownTextHeight = 2.0;
-        this.continuousTempoTextHeight = 2.3;
-        this.dynamicExpressionMaxDistance = 2;
-        this.dynamicExpressionSpacer = 0.5;
+        this.InstantaneousTempoTextHeight = 2.3;
+        this.ContinuousDynamicTextHeight = 2.3;
+        this.MoodTextHeight = 2.3;
+        this.UnknownTextHeight = 2.0;
+        this.ContinuousTempoTextHeight = 2.3;
+        this.DynamicExpressionMaxDistance = 2;
+        this.DynamicExpressionSpacer = 0.5;
 
         // Line Widths
-        this.staffLineWidth = 0.12;
-        this.ledgerLineWidth = 0.12;
-        this.wedgeLineWidth = 0.12;
-        this.tupletLineWidth = 0.12;
-        this.lyricUnderscoreLineWidth = 0.12;
-        this.systemThinLineWidth = 0.12;
-        this.systemBoldLineWidth = EngravingRules.unit / 2.0;
-        this.systemRepetitionEndingLineWidth = 0.12;
-        this.systemDotWidth = EngravingRules.unit / 5.0;
-        this.distanceBetweenVerticalSystemLines = 0.35;
-        this.distanceBetweenDotAndLine = 0.7;
-        this.octaveShiftLineWidth = 0.12;
-        this.octaveShiftVerticalLineLength = EngravingRules.unit;
-        this.graceLineWidth = this.staffLineWidth * this.GraceNoteScalingFactor;
+        this.VexFlowDefaultNotationFontScale = 39; // scales notes, including rests. default value 39 in Vexflow.
+        this.VexFlowDefaultTabFontScale = 39;
+        this.TremoloStrokeScale = 1;
+        this.TremoloYSpacingScale = 1;
+        this.StemWidth = 0.15; // originally 0.13. vexflow default 0.15. should probably be adjusted when increasing vexFlowDefaultNotationFontScale,
+        this.StaffLineWidth = 0.10; // originally 0.12, but this will be pixels in Vexflow (*10).
+        this.StaffLineColor = undefined; // if undefined, vexflow default (grey). not a width, but affects visual line clarity.
+        this.LedgerLineWidth = 1; // vexflow units (pixels). if not undefined, the vexflow default will be overwritten
+        this.LedgerLineStrokeStyle = undefined; // if not undefined, the vexflow default will be overwritten
+        this.LedgerLineColorDefault = "#000000"; // black, previously grey by default
+        this.WedgeLineWidth = 0.12;
+        this.TupletLineWidth = 0.12;
+        this.LyricUnderscoreLineWidth = 0.12;
+        this.SystemThinLineWidth = 0.12;
+        this.SystemBoldLineWidth = EngravingRules.unit / 2.0;
+        this.SystemRepetitionEndingLineWidth = 0.12;
+        this.SystemDotWidth = EngravingRules.unit / 5.0;
+        this.DistanceBetweenVerticalSystemLines = 0.35;
+        this.DistanceBetweenDotAndLine = 0.7;
+        this.OctaveShiftLineWidth = 0.12;
+        this.OctaveShiftVerticalLineLength = EngravingRules.unit;
+        this.GraceLineWidth = this.StaffLineWidth * this.GraceNoteScalingFactor;
+
+        this.MultipleRestMeasureDefaultWidth = 4;
 
         // Line Widths
-        this.minimumStaffLineDistance = 1.0;
-        this.minimumCrossedBeamDifferenceMargin = 0.0001;
+        this.MinimumCrossedBeamDifferenceMargin = 0.0001;
 
         // xSpacing Variables
-        this.displacedNoteMargin = 0.1;
-        this.minNoteDistance = 2.0;
-        this.subMeasureXSpacingThreshold = 35;
-        this.measureDynamicsMaxScalingFactor = 2.5;
-        this.wholeRestXShiftVexflow = -2.5; // VexFlow draws rest notes too far to the right
-        this.metronomeMarkXShift = -6; // our unit, is taken * unitInPixels
-        this.metronomeMarkYShift = -0.5;
+        this.VoiceSpacingMultiplierVexflow = 0.85;
+        this.VoiceSpacingAddendVexflow = 3.0;
+        this.DisplacedNoteMargin = 0.1;
+        this.MinNoteDistance = 2.0;
+        this.SubMeasureXSpacingThreshold = 35;
+        this.MeasureDynamicsMaxScalingFactor = 2.5;
+        this.WholeRestXShiftVexflow = -1.5; // VexFlow draws rest notes too far to the right
+        this.MetronomeMarksDrawn = true;
+        this.MetronomeMarkXShift = -6; // our unit, is taken * unitInPixels
+        this.MetronomeMarkYShift = -0.5;
+        this.SoftmaxFactorVexFlow = 15; // only applies to Vexflow 3.x. 15 seems like the sweet spot. Vexflow default is 100.
+        // if too high, score gets too big, especially half notes. with half note quarter quarter, the quarters get squeezed.
+        // if too low, smaller notes aren't positioned correctly.
 
         // Render options (whether to render specific or invisible elements)
-        this.coloringEnabled = true;
-        this.colorBeams = true;
-        this.colorFlags = true;
-        this.defaultColorNotehead = undefined; // undefined colors mean black
-        this.defaultColorRest = undefined;
-        this.defaultColorStem = undefined;
-        this.defaultColorLabel = undefined;
-        this.defaultColorTitle = undefined;
-        this.maxMeasureToDrawIndex = Number.MAX_VALUE;
-        this.renderComposer = true;
-        this.renderTitle = true;
-        this.renderSubtitle = true;
-        this.renderLyricist = true;
-        this.renderInstrumentNames = true;
-        this.renderFingerings = true;
-        this.fingeringPosition = PlacementEnum.Left; // easier to get bounding box, and safer for vertical layout
-        this.fingeringInsideStafflines = false;
+        this.AlignRests = AlignRestOption.Never; // 0 = false, 1 = true, 2 = auto
+        this.FillEmptyMeasuresWithWholeRest = FillEmptyMeasuresWithWholeRests.No;
+        this.ArpeggiosGoAcrossVoices = false; // safe option, as otherwise arpeggios will always go across all voices in Vexflow, which is often unwanted
+        this.RenderArpeggios = true;
+        this.RenderSlurs = true;
+        this.ColoringMode = ColoringMode.XML;
+        this.ColoringEnabled = true;
+        this.ColorStemsLikeNoteheads = false;
+        this.ColorBeams = true;
+        this.ColorFlags = true;
+        this.DefaultColorNotehead = "#000000"; // black. undefined is only black if a note's color hasn't been changed before.
+        this.DefaultColorRest = this.DefaultColorNotehead;
+        this.DefaultColorStem = this.DefaultColorNotehead;
+        this.DefaultColorLabel = this.DefaultColorNotehead;
+        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.MaxSystemToDrawNumber = Number.MAX_VALUE;
+        this.MaxPageToDrawNumber = Number.MAX_VALUE;
+        this.RenderComposer = true;
+        this.RenderTitle = true;
+        this.RenderSubtitle = true;
+        this.RenderLyricist = true;
+        this.RenderPartNames = true;
+        this.RenderPartAbbreviations = true;
+        this.RenderFingerings = true;
+        this.RenderMeasureNumbers = true;
+        this.RenderMeasureNumbersOnlyAtSystemStart = false;
+        this.UseXMLMeasureNumbers = true;
+        this.RenderLyrics = true;
+        this.RenderMultipleRestMeasures = true;
+        this.AutoGenerateMutipleRestMeasuresFromRestMeasures = true;
+        this.RenderTimeSignatures = true;
+        this.ArticulationPlacementFromXML = true;
+        this.FingeringPosition = PlacementEnum.Left; // easier to get bounding box, and safer for vertical layout
+        this.FingeringInsideStafflines = false;
+        this.FingeringLabelFontHeight = 1.7;
+        this.FingeringOffsetX = 0.0;
+        this.NewSystemAtXMLNewSystemAttribute = false;
+        this.NewPageAtXMLNewPageAttribute = false;
+        this.RestoreCursorAfterRerender = true;
+        this.StretchLastSystemLine = false;
 
-        this.populateDictionaries();
+        EngravingRules.FixStafflineBoundingBox = false; // TODO temporary workaround
+
+        this.PageFormat = PageFormat.UndefinedPageFormat; // default: undefined / 'infinite' height page, using the canvas'/container's width and height
+        this.PageBackgroundColor = undefined; // default: transparent. half-transparent white: #FFFFFF88"
+        this.RenderSingleHorizontalStaffline = false;
+        this.SpacingBetweenTextLines = 0;
+
+        // this.populateDictionaries(); // these values aren't used currently
         try {
-            this.maxInstructionsConstValue = this.ClefLeftMargin + this.ClefRightMargin + this.KeyRightMargin + this.RhythmRightMargin + 11;
-            //if (FontInfo.Info !== undefined) {
+            this.MaxInstructionsConstValue = this.ClefLeftMargin + this.ClefRightMargin + this.KeyRightMargin + this.RhythmRightMargin + 11;
+            //if (FontInfo.Info) {
             //    this.maxInstructionsConstValue += FontInfo.Info.getBoundingBox(MusicSymbol.G_CLEF).width
             //        + FontInfo.Info.getBoundingBox(MusicSymbol.FOUR).width
             //        + 7 * FontInfo.Info.getBoundingBox(MusicSymbol.SHARP).width;
@@ -419,1078 +561,130 @@ export class EngravingRules {
         } catch (ex) {
             log.info("EngravingRules()", ex);
         }
-
-    }
-    public static get Rules(): EngravingRules {
-        return EngravingRules.rules !== undefined ? EngravingRules.rules : (EngravingRules.rules = new EngravingRules());
-    }
-    public get SamplingUnit(): number {
-        return this.samplingUnit;
-    }
-    public get SheetTitleHeight(): number {
-        return this.sheetTitleHeight;
-    }
-    public set SheetTitleHeight(value: number) {
-        this.sheetTitleHeight = value;
-    }
-    public get SheetSubtitleHeight(): number {
-        return this.sheetSubtitleHeight;
-    }
-    public set SheetSubtitleHeight(value: number) {
-        this.sheetSubtitleHeight = value;
-    }
-    public get SheetMinimumDistanceBetweenTitleAndSubtitle(): number {
-        return this.sheetMinimumDistanceBetweenTitleAndSubtitle;
-    }
-    public set SheetMinimumDistanceBetweenTitleAndSubtitle(value: number) {
-        this.sheetMinimumDistanceBetweenTitleAndSubtitle = value;
-    }
-    public get SheetComposerHeight(): number {
-        return this.sheetComposerHeight;
-    }
-    public set SheetComposerHeight(value: number) {
-        this.sheetComposerHeight = value;
-    }
-    public get SheetAuthorHeight(): number {
-        return this.sheetAuthorHeight;
-    }
-    public set SheetAuthorHeight(value: number) {
-        this.sheetAuthorHeight = value;
-    }
-    public get PagePlacement(): PagePlacementEnum {
-        return this.pagePlacementEnum;
-    }
-    public set PagePlacement(value: PagePlacementEnum) {
-        this.pagePlacementEnum = value;
-    }
-    public get CompactMode(): boolean {
-        return this.compactMode;
-    }
-    public set CompactMode(value: boolean) {
-        this.compactMode = value;
-    }
-    public get PageHeight(): number {
-        return this.pageHeight;
-    }
-    public set PageHeight(value: number) {
-        this.pageHeight = value;
-    }
-    public get PageTopMargin(): number {
-        return this.pageTopMargin;
-    }
-    public set PageTopMargin(value: number) {
-        this.pageTopMargin = value;
-    }
-    public get PageTopMarginNarrow(): number {
-        return this.pageTopMarginNarrow;
-    }
-    public set PageTopMarginNarrow(value: number) {
-        this.pageTopMarginNarrow = value;
-    }
-    public get PageBottomMargin(): number {
-        return this.pageBottomMargin;
-    }
-    public set PageBottomMargin(value: number) {
-        this.pageBottomMargin = value;
-    }
-    public get PageLeftMargin(): number {
-        return this.pageLeftMargin;
-    }
-    public set PageLeftMargin(value: number) {
-        this.pageLeftMargin = value;
-    }
-    public get PageRightMargin(): number {
-        return this.pageRightMargin;
-    }
-    public set PageRightMargin(value: number) {
-        this.pageRightMargin = value;
-    }
-    public get TitleTopDistance(): number {
-        return this.titleTopDistance;
-    }
-    public set TitleTopDistance(value: number) {
-        this.titleTopDistance = value;
-    }
-    public get TitleBottomDistance(): number {
-        return this.titleBottomDistance;
-    }
-    public set TitleBottomDistance(value: number) {
-        this.titleBottomDistance = value;
-    }
-    public get SystemComposerDistance(): number {
-        return this.systemComposerDistance;
-    }
-    public set SystemComposerDistance(value: number) {
-        this.systemComposerDistance = value;
-    }
-    public get InstrumentLabelTextHeight(): number {
-        return this.instrumentLabelTextHeight;
-    }
-    public set InstrumentLabelTextHeight(value: number) {
-        this.instrumentLabelTextHeight = value;
-    }
-    public get SystemDistance(): number {
-        return this.systemDistance;
-    }
-    public set SystemDistance(value: number) {
-        this.systemDistance = value;
-    }
-    public get SystemLeftMargin(): number {
-        return this.systemLeftMargin;
-    }
-    public set SystemLeftMargin(value: number) {
-        this.systemLeftMargin = value;
-    }
-    public get SystemRightMargin(): number {
-        return this.systemRightMargin;
-    }
-    public set SystemRightMargin(value: number) {
-        this.systemRightMargin = value;
-    }
-    public get FirstSystemMargin(): number {
-        return this.firstSystemMargin;
-    }
-    public set FirstSystemMargin(value: number) {
-        this.firstSystemMargin = value;
-    }
-    public get SystemLabelsRightMargin(): number {
-        return this.systemLabelsRightMargin;
-    }
-    public set SystemLabelsRightMargin(value: number) {
-        this.systemLabelsRightMargin = value;
-    }
-    public get MinimumAllowedDistanceBetweenSystems(): number {
-        return this.minimumAllowedDistanceBetweenSystems;
-    }
-    public set MinimumAllowedDistanceBetweenSystems(value: number) {
-        this.minimumAllowedDistanceBetweenSystems = value;
-    }
-    public get LastSystemMaxScalingFactor(): number {
-        return this.lastSystemMaxScalingFactor;
-    }
-    public set LastSystemMaxScalingFactor(value: number) {
-        this.lastSystemMaxScalingFactor = value;
-    }
-    public get StaffDistance(): number {
-        return this.staffDistance;
-    }
-    public set StaffDistance(value: number) {
-        this.staffDistance = value;
-    }
-    public get BetweenStaffDistance(): number {
-        return this.betweenStaffDistance;
-    }
-    public set BetweenStaffDistance(value: number) {
-        this.betweenStaffDistance = value;
-    }
-    public get StaffHeight(): number {
-        return this.staffHeight;
-    }
-    public set StaffHeight(value: number) {
-        this.staffHeight = value;
-    }
-    public get BetweenStaffLinesDistance(): number {
-        return this.betweenStaffLinesDistance;
-    }
-    public set BetweenStaffLinesDistance(value: number) {
-        this.betweenStaffLinesDistance = value;
-    }
-    public get AutoBeamNotes(): boolean {
-        return this.autoBeamNotes;
-    }
-    public set AutoBeamNotes(value: boolean) {
-        this.autoBeamNotes = value;
-    }
-    public get AutoBeamOptions(): AutoBeamOptions {
-        return this.autoBeamOptions;
-    }
-    public set AutoBeamOptions(value: AutoBeamOptions) {
-        this.autoBeamOptions = value;
-    }
-    public get BeamWidth(): number {
-        return this.beamWidth;
-    }
-    public set BeamWidth(value: number) {
-        this.beamWidth = value;
-    }
-    public get BeamSpaceWidth(): number {
-        return this.beamSpaceWidth;
-    }
-    public set BeamSpaceWidth(value: number) {
-        this.beamSpaceWidth = value;
-    }
-    public get BeamForwardLength(): number {
-        return this.beamForwardLength;
-    }
-    public set BeamForwardLength(value: number) {
-        this.beamForwardLength = value;
-    }
-    public get BetweenKeySymbolsDistance(): number {
-        return this.betweenKeySymbolsDistance;
-    }
-    public set BetweenKeySymbolsDistance(value: number) {
-        this.betweenKeySymbolsDistance = value;
-    }
-    public get ClefLeftMargin(): number {
-        return this.clefLeftMargin;
-    }
-    public set ClefLeftMargin(value: number) {
-        this.clefLeftMargin = value;
-    }
-    public get ClefRightMargin(): number {
-        return this.clefRightMargin;
-    }
-    public set ClefRightMargin(value: number) {
-        this.clefRightMargin = value;
-    }
-    public get KeyRightMargin(): number {
-        return this.keyRightMargin;
-    }
-    public set KeyRightMargin(value: number) {
-        this.keyRightMargin = value;
-    }
-    public get RhythmRightMargin(): number {
-        return this.rhythmRightMargin;
-    }
-    public set RhythmRightMargin(value: number) {
-        this.rhythmRightMargin = value;
-    }
-    public get InStaffClefScalingFactor(): number {
-        return this.inStaffClefScalingFactor;
-    }
-    public set InStaffClefScalingFactor(value: number) {
-        this.inStaffClefScalingFactor = value;
-    }
-    public get DistanceBetweenNaturalAndSymbolWhenCancelling(): number {
-        return this.distanceBetweenNaturalAndSymbolWhenCancelling;
-    }
-    public set DistanceBetweenNaturalAndSymbolWhenCancelling(value: number) {
-        this.distanceBetweenNaturalAndSymbolWhenCancelling = value;
-    }
-    public get NoteHelperLinesOffset(): number {
-        return this.noteHelperLinesOffset;
-    }
-    public set NoteHelperLinesOffset(value: number) {
-        this.noteHelperLinesOffset = value;
-    }
-    public get MeasureLeftMargin(): number {
-        return this.measureLeftMargin;
-    }
-    public set MeasureLeftMargin(value: number) {
-        this.measureLeftMargin = value;
-    }
-    public get MeasureRightMargin(): number {
-        return this.measureRightMargin;
-    }
-    public set MeasureRightMargin(value: number) {
-        this.measureRightMargin = value;
-    }
-    public get DistanceBetweenLastInstructionAndRepetitionBarline(): number {
-        return this.distanceBetweenLastInstructionAndRepetitionBarline;
-    }
-    public set DistanceBetweenLastInstructionAndRepetitionBarline(value: number) {
-        this.distanceBetweenLastInstructionAndRepetitionBarline = value;
-    }
-    public get ArpeggioDistance(): number {
-        return this.arpeggioDistance;
-    }
-    public set ArpeggioDistance(value: number) {
-        this.arpeggioDistance = value;
-    }
-    public get StaccatoShorteningFactor(): number {
-        return this.staccatoShorteningFactor;
-    }
-    public set StaccatoShorteningFactor(value: number) {
-        this.staccatoShorteningFactor = value;
-    }
-    public get IdealStemLength(): number {
-        return this.idealStemLength;
-    }
-    public set IdealStemLength(value: number) {
-        this.idealStemLength = value;
-    }
-    public get StemNoteHeadBorderYOffset(): number {
-        return this.stemNoteHeadBorderYOffset;
-    }
-    public set StemNoteHeadBorderYOffset(value: number) {
-        this.stemNoteHeadBorderYOffset = value;
-    }
-    public get StemWidth(): number {
-        return this.stemWidth;
-    }
-    public set StemWidth(value: number) {
-        this.stemWidth = value;
-    }
-    public get StemMargin(): number {
-        return this.stemMargin;
-    }
-    public set StemMargin(value: number) {
-        this.stemMargin = value;
-    }
-    public get StemMinLength(): number {
-        return this.stemMinLength;
-    }
-    public set StemMinLength(value: number) {
-        this.stemMinLength = value;
-    }
-    public get StemMaxLength(): number {
-        return this.stemMaxLength;
-    }
-    public set StemMaxLength(value: number) {
-        this.stemMaxLength = value;
-    }
-    public get BeamSlopeMaxAngle(): number {
-        return this.beamSlopeMaxAngle;
-    }
-    public set BeamSlopeMaxAngle(value: number) {
-        this.beamSlopeMaxAngle = value;
-    }
-    public get StemMinAllowedDistanceBetweenNoteHeadAndBeamLine(): number {
-        return this.stemMinAllowedDistanceBetweenNoteHeadAndBeamLine;
-    }
-    public set StemMinAllowedDistanceBetweenNoteHeadAndBeamLine(value: number) {
-        this.stemMinAllowedDistanceBetweenNoteHeadAndBeamLine = value;
-    }
-    public get SetWantedStemDirectionByXml(): boolean {
-        return this.setWantedStemDirectionByXml;
-    }
-    public set SetWantedStemDirectionByXml(value: boolean) {
-        this.setWantedStemDirectionByXml = value;
-    }
-    public get GraceNoteScalingFactor(): number {
-        return this.graceNoteScalingFactor;
-    }
-    public set GraceNoteScalingFactor(value: number) {
-        this.graceNoteScalingFactor = value;
-    }
-    public get GraceNoteXOffset(): number {
-        return this.graceNoteXOffset;
-    }
-    public set GraceNoteXOffset(value: number) {
-        this.graceNoteXOffset = value;
-    }
-    public get WedgeOpeningLength(): number {
-        return this.wedgeOpeningLength;
-    }
-    public set WedgeOpeningLength(value: number) {
-        this.wedgeOpeningLength = value;
-    }
-    public get WedgeMeasureEndOpeningLength(): number {
-        return this.wedgeMeasureEndOpeningLength;
-    }
-    public set WedgeMeasureEndOpeningLength(value: number) {
-        this.wedgeMeasureEndOpeningLength = value;
-    }
-    public get WedgeMeasureBeginOpeningLength(): number {
-        return this.wedgeMeasureBeginOpeningLength;
-    }
-    public set WedgeMeasureBeginOpeningLength(value: number) {
-        this.wedgeMeasureBeginOpeningLength = value;
-    }
-    public get WedgePlacementAboveY(): number {
-        return this.wedgePlacementAboveY;
-    }
-    public set WedgePlacementAboveY(value: number) {
-        this.wedgePlacementAboveY = value;
-    }
-    public get WedgePlacementBelowY(): number {
-        return this.wedgePlacementBelowY;
-    }
-    public set WedgePlacementBelowY(value: number) {
-        this.wedgePlacementBelowY = value;
-    }
-    public get WedgeHorizontalMargin(): number {
-        return this.wedgeHorizontalMargin;
-    }
-    public set WedgeHorizontalMargin(value: number) {
-        this.wedgeHorizontalMargin = value;
-    }
-    public get WedgeVerticalMargin(): number {
-        return this.wedgeVerticalMargin;
-    }
-    public set WedgeVerticalMargin(value: number) {
-        this.wedgeVerticalMargin = value;
-    }
-    public get DistanceOffsetBetweenTwoHorizontallyCrossedWedges(): number {
-        return this.distanceOffsetBetweenTwoHorizontallyCrossedWedges;
-    }
-    public set DistanceOffsetBetweenTwoHorizontallyCrossedWedges(value: number) {
-        this.distanceOffsetBetweenTwoHorizontallyCrossedWedges = value;
-    }
-    public get WedgeMinLength(): number {
-        return this.wedgeMinLength;
-    }
-    public set WedgeMinLength(value: number) {
-        this.wedgeMinLength = value;
-    }
-    public get DistanceBetweenAdjacentDynamics(): number {
-        return this.distanceBetweenAdjacentDynamics;
-    }
-    public set DistanceBetweenAdjacentDynamics(value: number) {
-        this.distanceBetweenAdjacentDynamics = value;
-    }
-    public get TempoChangeMeasureValidity(): number {
-        return this.tempoChangeMeasureValidity;
-    }
-    public set TempoChangeMeasureValidity(value: number) {
-        this.tempoChangeMeasureValidity = value;
-    }
-    public get TempoContinousFactor(): number {
-        return this.tempoContinousFactor;
-    }
-    public set TempoContinousFactor(value: number) {
-        this.tempoContinousFactor = value;
-    }
-    public get StaccatoScalingFactor(): number {
-        return this.staccatoScalingFactor;
-    }
-    public set StaccatoScalingFactor(value: number) {
-        this.staccatoScalingFactor = value;
-    }
-    public get BetweenDotsDistance(): number {
-        return this.betweenDotsDistance;
-    }
-    public set BetweenDotsDistance(value: number) {
-        this.betweenDotsDistance = value;
-    }
-    public get OrnamentAccidentalScalingFactor(): number {
-        return this.ornamentAccidentalScalingFactor;
-    }
-    public set OrnamentAccidentalScalingFactor(value: number) {
-        this.ornamentAccidentalScalingFactor = value;
-    }
-    public get ChordSymbolTextHeight(): number {
-        return this.chordSymbolTextHeight;
-    }
-    public set ChordSymbolTextHeight(value: number) {
-        this.chordSymbolTextHeight = value;
-    }
-    public get ChordSymbolYOffset(): number {
-        return this.chordSymbolYOffset;
-    }
-    public set ChordSymbolYOffset(value: number) {
-        this.chordSymbolYOffset = value;
-    }
-    public get FingeringLabelFontHeight(): number {
-        return this.fingeringLabelFontHeight;
-    }
-    public set FingeringLabelFontHeight(value: number) {
-        this.fingeringLabelFontHeight = value;
-    }
-    public get MeasureNumberLabelHeight(): number {
-        return this.measureNumberLabelHeight;
-    }
-    public set MeasureNumberLabelHeight(value: number) {
-        this.measureNumberLabelHeight = value;
-    }
-    public get MeasureNumberLabelOffset(): number {
-        return this.measureNumberLabelOffset;
-    }
-    public set MeasureNumberLabelOffset(value: number) {
-        this.measureNumberLabelOffset = value;
-    }
-    public get TupletsRatioed(): boolean {
-        return this.tupletsRatioed;
-    }
-    public set TupletsRatioed(value: boolean) {
-        this.tupletsRatioed = value;
-    }
-    public get TupletsBracketed(): boolean {
-        return this.tupletsBracketed;
-    }
-    public set TupletsBracketed(value: boolean) {
-        this.tupletsBracketed = value;
-    }
-    public get TripletsBracketed(): boolean {
-        return this.tripletsBracketed;
-    }
-    public set TripletsBracketed(value: boolean) {
-        this.tripletsBracketed = value;
-    }
-    public get TupletNumberLabelHeight(): number {
-        return this.tupletNumberLabelHeight;
-    }
-    public set TupletNumberLabelHeight(value: number) {
-        this.tupletNumberLabelHeight = value;
-    }
-    public get TupletNumberYOffset(): number {
-        return this.tupletNumberYOffset;
-    }
-    public set TupletNumberYOffset(value: number) {
-        this.tupletNumberYOffset = value;
-    }
-    public get LabelMarginBorderFactor(): number {
-        return this.labelMarginBorderFactor;
-    }
-    public set LabelMarginBorderFactor(value: number) {
-        this.labelMarginBorderFactor = value;
-    }
-    public get TupletVerticalLineLength(): number {
-        return this.tupletVerticalLineLength;
-    }
-    public set TupletVerticalLineLength(value: number) {
-        this.tupletVerticalLineLength = value;
-    }
-    public get RepetitionEndingLabelHeight(): number {
-        return this.repetitionEndingLabelHeight;
-    }
-    public set RepetitionEndingLabelHeight(value: number) {
-        this.repetitionEndingLabelHeight = value;
-    }
-    public get RepetitionEndingLabelXOffset(): number {
-        return this.repetitionEndingLabelXOffset;
-    }
-    public set RepetitionEndingLabelXOffset(value: number) {
-        this.repetitionEndingLabelXOffset = value;
-    }
-    public get RepetitionEndingLabelYOffset(): number {
-        return this.repetitionEndingLabelYOffset;
-    }
-    public set RepetitionEndingLabelYOffset(value: number) {
-        this.repetitionEndingLabelYOffset = value;
-    }
-    public get RepetitionEndingLineYLowerOffset(): number {
-        return this.repetitionEndingLineYLowerOffset;
-    }
-    public set RepetitionEndingLineYLowerOffset(value: number) {
-        this.repetitionEndingLineYLowerOffset = value;
-    }
-    public get RepetitionEndingLineYUpperOffset(): number {
-        return this.repetitionEndingLineYUpperOffset;
-    }
-    public set RepetitionEndingLineYUpperOffset(value: number) {
-        this.repetitionEndingLineYUpperOffset = value;
-    }
-    public get LyricsAlignmentStandard(): TextAlignmentEnum {
-        return this.lyricsAlignmentStandard;
-    }
-    public set LyricsAlignmentStandard(value: TextAlignmentEnum) {
-        this.lyricsAlignmentStandard = value;
-    }
-    public get LyricsHeight(): number {
-        return this.lyricsHeight;
-    }
-    public set LyricsHeight(value: number) {
-        this.lyricsHeight = value;
-    }
-    public get LyricsYOffsetToStaffHeight(): number {
-        return this.lyricsYOffsetToStaffHeight;
-    }
-    public set LyricsYOffsetToStaffHeight(value: number) {
-        this.lyricsYOffsetToStaffHeight = value;
-    }
-    public get VerticalBetweenLyricsDistance(): number {
-        return this.verticalBetweenLyricsDistance;
-    }
-    public set VerticalBetweenLyricsDistance(value: number) {
-        this.verticalBetweenLyricsDistance = value;
-    }
-    public get HorizontalBetweenLyricsDistance(): number {
-        return this.horizontalBetweenLyricsDistance;
-    }
-    public set HorizontalBetweenLyricsDistance(value: number) {
-        this.horizontalBetweenLyricsDistance = value;
-    }
-    public get BetweenSyllableMaximumDistance(): number {
-        return this.betweenSyllableMaximumDistance;
-    }
-    public set BetweenSyllableMaximumDistance(value: number) {
-        this.betweenSyllableMaximumDistance = value;
-    }
-    public get BetweenSyllableMinimumDistance(): number {
-        return this.betweenSyllableMinimumDistance;
-    }
-    public set BetweenSyllableMinimumDistance(value: number) {
-        this.betweenSyllableMinimumDistance = value;
-    }
-    public get LyricOverlapAllowedIntoNextMeasure(): number {
-        return this.lyricOverlapAllowedIntoNextMeasure;
-    }
-    public set LyricOverlapAllowedIntoNextMeasure(value: number) {
-        this.lyricOverlapAllowedIntoNextMeasure = value;
-    }
-    public get MinimumDistanceBetweenDashes(): number {
-        return this.minimumDistanceBetweenDashes;
-    }
-    public set MinimumDistanceBetweenDashes(value: number) {
-        this.minimumDistanceBetweenDashes = value;
-    }
-    public get BezierCurveStepSize(): number {
-        return this.bezierCurveStepSize;
-    }
-    public set BezierCurveStepSize(value: number) {
-        this.bezierCurveStepSize = value;
-    }
-    public get TPow3(): number[] {
-        return this.tPower3;
-    }
-    public set TPow3(value: number[]) {
-        this.tPower3 = value;
-    }
-    public get OneMinusTPow3(): number[] {
-        return this.oneMinusTPower3;
-    }
-    public set OneMinusTPow3(value: number[]) {
-        this.oneMinusTPower3 = value;
-    }
-    public get BezierFactorOne(): number[] {
-        return this.factorOne;
-    }
-    public set BezierFactorOne(value: number[]) {
-        this.factorOne = value;
-    }
-    public get BezierFactorTwo(): number[] {
-        return this.factorTwo;
-    }
-    public set BezierFactorTwo(value: number[]) {
-        this.factorTwo = value;
-    }
-    public get TieGhostObjectWidth(): number {
-        return this.tieGhostObjectWidth;
-    }
-    public set TieGhostObjectWidth(value: number) {
-        this.tieGhostObjectWidth = value;
-    }
-    public get TieYPositionOffsetFactor(): number {
-        return this.tieYPositionOffsetFactor;
-    }
-    public set TieYPositionOffsetFactor(value: number) {
-        this.tieYPositionOffsetFactor = value;
-    }
-    public get MinimumNeededXspaceForTieGhostObject(): number {
-        return this.minimumNeededXspaceForTieGhostObject;
-    }
-    public set MinimumNeededXspaceForTieGhostObject(value: number) {
-        this.minimumNeededXspaceForTieGhostObject = value;
-    }
-    public get TieHeightMinimum(): number {
-        return this.tieHeightMinimum;
-    }
-    public set TieHeightMinimum(value: number) {
-        this.tieHeightMinimum = value;
-    }
-    public get TieHeightMaximum(): number {
-        return this.tieHeightMaximum;
-    }
-    public set TieHeightMaximum(value: number) {
-        this.tieHeightMaximum = value;
-    }
-    public get TieHeightInterpolationK(): number {
-        return this.tieHeightInterpolationK;
-    }
-    public set TieHeightInterpolationK(value: number) {
-        this.tieHeightInterpolationK = value;
-    }
-    public get TieHeightInterpolationD(): number {
-        return this.tieHeightInterpolationD;
-    }
-    public set TieHeightInterpolationD(value: number) {
-        this.tieHeightInterpolationD = value;
-    }
-    public get SlurNoteHeadYOffset(): number {
-        return this.slurNoteHeadYOffset;
-    }
-    public set SlurNoteHeadYOffset(value: number) {
-        this.slurNoteHeadYOffset = value;
-    }
-    public get SlurStemXOffset(): number {
-        return this.slurStemXOffset;
-    }
-    public set SlurStemXOffset(value: number) {
-        this.slurStemXOffset = value;
-    }
-    public get SlurSlopeMaxAngle(): number {
-        return this.slurSlopeMaxAngle;
-    }
-    public set SlurSlopeMaxAngle(value: number) {
-        this.slurSlopeMaxAngle = value;
-    }
-    public get SlurTangentMinAngle(): number {
-        return this.slurTangentMinAngle;
-    }
-    public set SlurTangentMinAngle(value: number) {
-        this.slurTangentMinAngle = value;
-    }
-    public get SlurTangentMaxAngle(): number {
-        return this.slurTangentMaxAngle;
-    }
-    public set SlurTangentMaxAngle(value: number) {
-        this.slurTangentMaxAngle = value;
-    }
-    public get SlursStartingAtSameStaffEntryYOffset(): number {
-        return this.slursStartingAtSameStaffEntryYOffset;
-    }
-    public set SlursStartingAtSameStaffEntryYOffset(value: number) {
-        this.slursStartingAtSameStaffEntryYOffset = value;
-    }
-    public get InstantaneousTempoTextHeight(): number {
-        return this.instantaneousTempoTextHeight;
-    }
-    public set InstantaneousTempoTextHeight(value: number) {
-        this.instantaneousTempoTextHeight = value;
-    }
-    public get ContinuousDynamicTextHeight(): number {
-        return this.continuousDynamicTextHeight;
-    }
-    public set ContinuousDynamicTextHeight(value: number) {
-        this.continuousDynamicTextHeight = value;
-    }
-    public get MoodTextHeight(): number {
-        return this.moodTextHeight;
-    }
-    public set MoodTextHeight(value: number) {
-        this.moodTextHeight = value;
-    }
-    public get ContinuousTempoTextHeight(): number {
-        return this.continuousTempoTextHeight;
-    }
-    public set ContinuousTempoTextHeight(value: number) {
-        this.continuousTempoTextHeight = value;
-    }
-    /** Distance of expressions inside a group */
-    public get DynamicExpressionMaxDistance(): number {
-        return this.dynamicExpressionMaxDistance;
-    }
-    public set DynamicExpressionMaxDistance(value: number) {
-        this.dynamicExpressionMaxDistance = value;
-    }
-    /** Space between expressions in a group */
-    public get DynamicExpressionSpacer(): number {
-        return this.dynamicExpressionSpacer;
-    }
-    public set DynamicExpressionSpacer(value: number) {
-        this.dynamicExpressionSpacer = value;
     }
 
-    public get UnknownTextHeight(): number {
-        return this.unknownTextHeight;
-    }
-    public set UnknownTextHeight(value: number) {
-        this.unknownTextHeight = value;
-    }
-    public get StaffLineWidth(): number {
-        return this.staffLineWidth;
-    }
-    public set StaffLineWidth(value: number) {
-        this.staffLineWidth = value;
-    }
-    public get LedgerLineWidth(): number {
-        return this.ledgerLineWidth;
-    }
-    public set LedgerLineWidth(value: number) {
-        this.ledgerLineWidth = value;
-    }
-    public get WedgeLineWidth(): number {
-        return this.wedgeLineWidth;
-    }
-    public set WedgeLineWidth(value: number) {
-        this.wedgeLineWidth = value;
-    }
-    public get TupletLineWidth(): number {
-        return this.tupletLineWidth;
-    }
-    public set TupletLineWidth(value: number) {
-        this.tupletLineWidth = value;
-    }
-    public get LyricUnderscoreLineWidth(): number {
-        return this.lyricUnderscoreLineWidth;
-    }
-    public set LyricUnderscoreLineWidth(value: number) {
-        this.lyricUnderscoreLineWidth = value;
-    }
-    public get SystemThinLineWidth(): number {
-        return this.systemThinLineWidth;
-    }
-    public set SystemThinLineWidth(value: number) {
-        this.systemThinLineWidth = value;
-    }
-    public get SystemBoldLineWidth(): number {
-        return this.systemBoldLineWidth;
-    }
-    public set SystemBoldLineWidth(value: number) {
-        this.systemBoldLineWidth = value;
-    }
-    public get SystemRepetitionEndingLineWidth(): number {
-        return this.systemRepetitionEndingLineWidth;
-    }
-    public set SystemRepetitionEndingLineWidth(value: number) {
-        this.systemRepetitionEndingLineWidth = value;
-    }
-    public get SystemDotWidth(): number {
-        return this.systemDotWidth;
-    }
-    public set SystemDotWidth(value: number) {
-        this.systemDotWidth = value;
-    }
-    public get DistanceBetweenVerticalSystemLines(): number {
-        return this.distanceBetweenVerticalSystemLines;
-    }
-    public set DistanceBetweenVerticalSystemLines(value: number) {
-        this.distanceBetweenVerticalSystemLines = value;
-    }
-    public get DistanceBetweenDotAndLine(): number {
-        return this.distanceBetweenDotAndLine;
-    }
-    public set DistanceBetweenDotAndLine(value: number) {
-        this.distanceBetweenDotAndLine = value;
-    }
-    public get OctaveShiftLineWidth(): number {
-        return this.octaveShiftLineWidth;
-    }
-    public set OctaveShiftLineWidth(value: number) {
-        this.octaveShiftLineWidth = value;
-    }
-    public get OctaveShiftVerticalLineLength(): number {
-        return this.octaveShiftVerticalLineLength;
-    }
-    public set OctaveShiftVerticalLineLength(value: number) {
-        this.octaveShiftVerticalLineLength = value;
-    }
-    public get GraceLineWidth(): number {
-        return this.graceLineWidth;
-    }
-    public set GraceLineWidth(value: number) {
-        this.graceLineWidth = value;
-    }
-    public get MinimumStaffLineDistance(): number {
-        return this.minimumStaffLineDistance;
-    }
-    public set MinimumStaffLineDistance(value: number) {
-        this.minimumStaffLineDistance = value;
-    }
-    public get MinimumCrossedBeamDifferenceMargin(): number {
-        return this.minimumCrossedBeamDifferenceMargin;
-    }
-    public set MinimumCrossedBeamDifferenceMargin(value: number) {
-        this.minimumCrossedBeamDifferenceMargin = value;
-    }
-    public get DisplacedNoteMargin(): number {
-        return this.displacedNoteMargin;
-    }
-    public set DisplacedNoteMargin(value: number) {
-        this.displacedNoteMargin = value;
-    }
-    public get MinNoteDistance(): number {
-        return this.minNoteDistance;
-    }
-    public set MinNoteDistance(value: number) {
-        this.minNoteDistance = value;
-    }
-    public get SubMeasureXSpacingThreshold(): number {
-        return this.subMeasureXSpacingThreshold;
-    }
-    public set SubMeasureXSpacingThreshold(value: number) {
-        this.subMeasureXSpacingThreshold = value;
-    }
-    public get MeasureDynamicsMaxScalingFactor(): number {
-        return this.measureDynamicsMaxScalingFactor;
-    }
-    public set MeasureDynamicsMaxScalingFactor(value: number) {
-        this.measureDynamicsMaxScalingFactor = value;
-    }
-    public get WholeRestXShiftVexflow(): number {
-        return this.wholeRestXShiftVexflow;
-    }
-    public set WholeRestXShiftVexflow(value: number) {
-        this.wholeRestXShiftVexflow = value;
-    }
-    public get MetronomeMarkXShift(): number {
-        return this.metronomeMarkXShift;
-    }
-    public set MetronomeMarkXShift(value: number) {
-        this.metronomeMarkXShift = value;
-    }
-    public get MetronomeMarkYShift(): number {
-        return this.metronomeMarkYShift;
-    }
-    public set MetronomeMarkYShift(value: number) {
-        this.metronomeMarkYShift = value;
-    }
-    public get MaxInstructionsConstValue(): number {
-        return this.maxInstructionsConstValue;
-    }
-    public set MaxInstructionsConstValue(value: number) {
-        this.maxInstructionsConstValue = value;
-    }
-    public get NoteDistances(): number[] {
-        return this.noteDistances;
-    }
-    public set NoteDistances(value: number[]) {
-        this.noteDistances = value;
-    }
-    public get NoteDistancesScalingFactors(): number[] {
-        return this.noteDistancesScalingFactors;
-    }
-    public set NoteDistancesScalingFactors(value: number[]) {
-        this.noteDistancesScalingFactors = value;
-    }
-    public get DurationDistanceDict(): {[_: number]: number; } {
-        return this.durationDistanceDict;
-    }
-    public get DurationScalingDistanceDict(): {[_: number]: number; } {
-        return this.durationScalingDistanceDict;
-    }
-    public get ColoringEnabled(): boolean {
-        return this.coloringEnabled;
-    }
-    public set ColoringEnabled(value: boolean) {
-        this.coloringEnabled = value;
-    }
-    public get ColorFlags(): boolean {
-        return this.colorFlags;
-    }
-    public set ColorFlags(value: boolean) {
-        this.colorFlags = value;
-    }
-    public get ColorBeams(): boolean {
-        return this.colorBeams;
-    }
-    public set ColorBeams(value: boolean) {
-        this.colorBeams = value;
-    }
-    public get DefaultColorNotehead(): string {
-        return this.defaultColorNotehead;
-    }
-    public set DefaultColorNotehead(value: string) {
-        this.defaultColorNotehead = value;
-    }
-    public get DefaultColorRest(): string {
-        return this.defaultColorRest;
-    }
-    public set DefaultColorRest(value: string) {
-        this.defaultColorRest = value;
-    }
-    public get DefaultColorStem(): string {
-        return this.defaultColorStem;
-    }
-    public set DefaultColorStem(value: string) {
-        this.defaultColorStem = value;
-    }
-    public get DefaultColorLabel(): string {
-        return this.defaultColorLabel;
-    }
-    public set DefaultColorLabel(value: string) {
-        this.defaultColorLabel = value;
-    }
-    public get DefaultColorTitle(): string {
-        return this.defaultColorTitle;
-    }
-    public set DefaultColorTitle(value: string) {
-        this.defaultColorTitle = value;
-    }
-    public get MaxMeasureToDrawIndex(): number {
-        return this.maxMeasureToDrawIndex;
-    }
-    public set MaxMeasureToDrawIndex(value: number) {
-        this.maxMeasureToDrawIndex = value;
-    }
-    public get RenderComposer(): boolean {
-        return this.renderComposer;
-    }
-    public set RenderComposer(value: boolean) {
-        this.renderComposer = value;
-    }
-    public get RenderTitle(): boolean {
-        return this.renderTitle;
-    }
-    public set RenderTitle(value: boolean) {
-        this.renderTitle = value;
-    }
-    public get RenderSubtitle(): boolean {
-        return this.renderSubtitle;
-    }
-    public set RenderSubtitle(value: boolean) {
-        this.renderSubtitle = value;
-    }
-    public get RenderLyricist(): boolean {
-        return this.renderLyricist;
-    }
-    public set RenderLyricist(value: boolean) {
-        this.renderLyricist = value;
-    }
-    public get RenderInstrumentNames(): boolean {
-        return this.renderInstrumentNames;
-    }
-    public set RenderInstrumentNames(value: boolean) {
-        this.renderInstrumentNames = value;
-    }
-    public get RenderFingerings(): boolean {
-        return this.renderFingerings;
-    }
-    public set RenderFingerings(value: boolean) {
-        this.renderFingerings = value;
-    }
-    public get FingeringPosition(): PlacementEnum {
-        return this.fingeringPosition;
-    }
-    public set FingeringPosition(value: PlacementEnum) {
-        this.fingeringPosition = value;
-    }
-    public get FingeringInsideStafflines(): boolean {
-        return this.fingeringInsideStafflines;
-    }
-    public set FingeringInsideStafflines(value: boolean) {
-        this.fingeringInsideStafflines = value;
+    public setChordSymbolLabelText(key: ChordSymbolEnum, value: string): void {
+        this.ChordSymbolLabelTexts.setValue(key, value);
+    }
+    public resetChordSymbolLabelTexts(chordtexts: Dictionary<ChordSymbolEnum, string>): Dictionary<ChordSymbolEnum, string> {
+        chordtexts.setValue(ChordSymbolEnum.minor, "m");
+        chordtexts.setValue(ChordSymbolEnum.augmented, "aug");
+        chordtexts.setValue(ChordSymbolEnum.diminished, "dim");
+        chordtexts.setValue(ChordSymbolEnum.dominant, "7");
+        chordtexts.setValue(ChordSymbolEnum.majorseventh, "maj7");
+        chordtexts.setValue(ChordSymbolEnum.minorseventh, "m7");
+        chordtexts.setValue(ChordSymbolEnum.diminishedseventh, "dim7");
+        chordtexts.setValue(ChordSymbolEnum.augmentedseventh, "aug7");
+        chordtexts.setValue(ChordSymbolEnum.halfdiminished, "m7b5");
+        chordtexts.setValue(ChordSymbolEnum.majorminor, "m(maj7)");
+        chordtexts.setValue(ChordSymbolEnum.majorsixth, "maj6");
+        chordtexts.setValue(ChordSymbolEnum.minorsixth, "m6");
+        chordtexts.setValue(ChordSymbolEnum.dominantninth, "9");
+        chordtexts.setValue(ChordSymbolEnum.majorninth, "maj9");
+        chordtexts.setValue(ChordSymbolEnum.minorninth, "m9");
+        chordtexts.setValue(ChordSymbolEnum.dominant11th, "11");
+        chordtexts.setValue(ChordSymbolEnum.major11th, "maj11");
+        chordtexts.setValue(ChordSymbolEnum.minor11th, "m11");
+        chordtexts.setValue(ChordSymbolEnum.dominant13th, "13");
+        chordtexts.setValue(ChordSymbolEnum.major13th, "maj13");
+        chordtexts.setValue(ChordSymbolEnum.minor13th, "m13");
+        chordtexts.setValue(ChordSymbolEnum.suspendedsecond, "sus2");
+        chordtexts.setValue(ChordSymbolEnum.suspendedfourth, "sus4");
+        chordtexts.setValue(ChordSymbolEnum.power, "5");
+
+        return chordtexts;
     }
 
     /**
      * This method maps NoteDurations to Distances and DistancesScalingFactors.
      */
-    private populateDictionaries(): void {
-        for (let i: number = 0; i < this.noteDistances.length; i++) {
-            switch (i) {
-                case 0:
-                    this.durationDistanceDict[0.015625] = this.noteDistances[i];
-                    this.durationScalingDistanceDict[0.015625] = this.noteDistancesScalingFactors[i];
-                    break;
-                case 1:
-                    this.durationDistanceDict[0.03125] = this.noteDistances[i];
-                    this.durationScalingDistanceDict[0.03125] = this.noteDistancesScalingFactors[i];
-                    break;
-                case 2:
-                    this.durationDistanceDict[0.0625] = this.noteDistances[i];
-                    this.durationScalingDistanceDict[0.0625] = this.noteDistancesScalingFactors[i];
-                    break;
-                case 3:
-                    this.durationDistanceDict[0.125] = this.noteDistances[i];
-                    this.durationScalingDistanceDict[0.125] = this.noteDistancesScalingFactors[i];
-                    break;
-                case 4:
-                    this.durationDistanceDict[0.25] = this.noteDistances[i];
-                    this.durationScalingDistanceDict[0.25] = this.noteDistancesScalingFactors[i];
-                    break;
-                case 5:
-                    this.durationDistanceDict[0.5] = this.noteDistances[i];
-                    this.durationScalingDistanceDict[0.5] = this.noteDistancesScalingFactors[i];
-                    break;
-                case 6:
-                    this.durationDistanceDict[1.0] = this.noteDistances[i];
-                    this.durationScalingDistanceDict[1.0] = this.noteDistancesScalingFactors[i];
-                    break;
-                case 7:
-                    this.durationDistanceDict[2.0] = this.noteDistances[i];
-                    this.durationScalingDistanceDict[2.0] = this.noteDistancesScalingFactors[i];
-                    break;
-                default:
-                    // FIXME
-            }
-        }
-    }
+    // private populateDictionaries(): void {
+    //     for (let i: number = 0; i < this.NoteDistances.length; i++) {
+    //         switch (i) {
+    //             case 0:
+    //                 this.DurationDistanceDict[0.015625] = this.NoteDistances[i];
+    //                 this.DurationScalingDistanceDict[0.015625] = this.NoteDistancesScalingFactors[i];
+    //                 break;
+    //             case 1:
+    //                 this.DurationDistanceDict[0.03125] = this.NoteDistances[i];
+    //                 this.DurationScalingDistanceDict[0.03125] = this.NoteDistancesScalingFactors[i];
+    //                 break;
+    //             case 2:
+    //                 this.DurationDistanceDict[0.0625] = this.NoteDistances[i];
+    //                 this.DurationScalingDistanceDict[0.0625] = this.NoteDistancesScalingFactors[i];
+    //                 break;
+    //             case 3:
+    //                 this.DurationDistanceDict[0.125] = this.NoteDistances[i];
+    //                 this.DurationScalingDistanceDict[0.125] = this.NoteDistancesScalingFactors[i];
+    //                 break;
+    //             case 4:
+    //                 this.DurationDistanceDict[0.25] = this.NoteDistances[i];
+    //                 this.DurationScalingDistanceDict[0.25] = this.NoteDistancesScalingFactors[i];
+    //                 break;
+    //             case 5:
+    //                 this.DurationDistanceDict[0.5] = this.NoteDistances[i];
+    //                 this.DurationScalingDistanceDict[0.5] = this.NoteDistancesScalingFactors[i];
+    //                 break;
+    //             case 6:
+    //                 this.DurationDistanceDict[1.0] = this.NoteDistances[i];
+    //                 this.DurationScalingDistanceDict[1.0] = this.NoteDistancesScalingFactors[i];
+    //                 break;
+    //             case 7:
+    //                 this.DurationDistanceDict[2.0] = this.NoteDistances[i];
+    //                 this.DurationScalingDistanceDict[2.0] = this.NoteDistancesScalingFactors[i];
+    //                 break;
+    //             default:
+    //                 // FIXME
+    //         }
+    //     }
+    // }
 
     /**
      * Calculate Curve-independend factors, to be used later in the Slur- and TieCurvePoints calculation
      */
     private calculateCurveParametersArrays(): void {
-        this.tPower3 = new Array(this.bezierCurveStepSize);
-        this.oneMinusTPower3 = new Array(this.bezierCurveStepSize);
-        this.factorOne = new Array(this.bezierCurveStepSize);
-        this.factorTwo = new Array(this.bezierCurveStepSize);
-        for (let i: number = 0; i < this.bezierCurveStepSize; i++) {
-            const t: number = i / this.bezierCurveStepSize;
-            this.tPower3[i] = Math.pow(t, 3);
-            this.oneMinusTPower3[i] = Math.pow((1 - t), 3);
-            this.factorOne[i] = 3 * Math.pow((1 - t), 2) * t;
-            this.factorTwo[i] = 3 * (1 - t) * Math.pow(t, 2);
+        this.TPower3 = new Array(this.BezierCurveStepSize);
+        this.OneMinusTPower3 = new Array(this.BezierCurveStepSize);
+        this.FactorOne = new Array(this.BezierCurveStepSize);
+        this.FactorTwo = new Array(this.BezierCurveStepSize);
+        for (let i: number = 0; i < this.BezierCurveStepSize; i++) {
+            const t: number = i / this.BezierCurveStepSize;
+            this.TPower3[i] = Math.pow(t, 3);
+            this.OneMinusTPower3[i] = Math.pow((1 - t), 3);
+            this.FactorOne[i] = 3 * Math.pow((1 - t), 2) * t;
+            this.FactorTwo[i] = 3 * (1 - t) * Math.pow(t, 2);
+        }
+    }
+}
+
+// TODO maybe this should be moved to OSMDOptions. Also see OpenSheetMusicDisplay.PageFormatStandards
+export class PageFormat {
+    constructor(width: number, height: number, idString: string = "noIdStringGiven") {
+        this.width = width;
+        this.height = height;
+        this.idString = idString;
+    }
+    public width: number;
+    public height: number;
+    public idString: string;
+    public get aspectRatio(): number {
+        if (!this.IsUndefined) {
+            return this.width / this.height;
+        } else {
+            return 0; // infinite page height
+        }
+    }
+    /** Undefined page format: use default page format. */
+    public get IsUndefined(): boolean {
+        return this.width === undefined || this.height === undefined || this.height === 0 || this.width === 0;
+    }
+    public static get UndefinedPageFormat(): PageFormat {
+        return new PageFormat(0, 0);
+    }
+    public Equals(otherPageFormat: PageFormat): boolean {
+        if (!otherPageFormat) {
+            return false;
         }
+        return otherPageFormat.width === this.width && otherPageFormat.height === this.height;
     }
 }

+ 11 - 5
src/MusicalScore/Graphical/GraphicalChordSymbolContainer.ts

@@ -5,15 +5,21 @@ import {ChordSymbolContainer} from "../VoiceData/ChordSymbolContainer";
 import {BoundingBox} from "./BoundingBox";
 import {GraphicalObject} from "./GraphicalObject";
 import {PointF2D} from "../../Common/DataObjects/PointF2D";
+import {EngravingRules} from "./EngravingRules";
+import { KeyInstruction } from "../VoiceData/Instructions/KeyInstruction";
 
 export class GraphicalChordSymbolContainer extends GraphicalObject {
     private chordSymbolContainer: ChordSymbolContainer;
     private graphicalLabel: GraphicalLabel;
-    constructor(chordSymbolContainer: ChordSymbolContainer, parent: BoundingBox, textHeight: number, transposeHalftones: number) {
+    private rules: EngravingRules;
+
+    constructor(chordSymbolContainer: ChordSymbolContainer, parent: BoundingBox, textHeight: number,
+                keyInstruction: KeyInstruction, transposeHalftones: number, rules: EngravingRules) {
         super();
         this.chordSymbolContainer = chordSymbolContainer;
         this.boundingBox = new BoundingBox(this, parent);
-        this.calculateLabel(textHeight, transposeHalftones);
+        this.rules = rules;
+        this.calculateLabel(textHeight, transposeHalftones, keyInstruction);
     }
     public get GetChordSymbolContainer(): ChordSymbolContainer {
         return this.chordSymbolContainer;
@@ -21,9 +27,9 @@ export class GraphicalChordSymbolContainer extends GraphicalObject {
     public get GetGraphicalLabel(): GraphicalLabel {
         return this.graphicalLabel;
     }
-    private calculateLabel(textHeight: number, transposeHalftones: number): void {
-        const text: string = ChordSymbolContainer.calculateChordText(this.chordSymbolContainer, transposeHalftones);
-        this.graphicalLabel = new GraphicalLabel(new Label(text), textHeight, TextAlignmentEnum.CenterBottom, this.boundingBox);
+    private calculateLabel(textHeight: number, transposeHalftones: number, keyInstruction: KeyInstruction): void {
+        const text: string = ChordSymbolContainer.calculateChordText(this.chordSymbolContainer, transposeHalftones, keyInstruction);
+        this.graphicalLabel = new GraphicalLabel(new Label(text), textHeight, TextAlignmentEnum.CenterBottom, this.rules, this.boundingBox);
         this.graphicalLabel.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
     }
 }

+ 29 - 7
src/MusicalScore/Graphical/GraphicalContinuousDynamicExpression.ts

@@ -7,7 +7,8 @@ import { AbstractGraphicalExpression } from "./AbstractGraphicalExpression";
 import { PlacementEnum } from "../VoiceData/Expressions/AbstractExpression";
 import { SkyBottomLineCalculator } from "./SkyBottomLineCalculator";
 import { ISqueezable } from "./ISqueezable";
-import * as log from "loglevel";
+import log from "loglevel";
+import { SourceMeasure } from "../VoiceData/SourceMeasure";
 
 /**
  * This class prepares the graphical elements for a continuous expression. It calculates the wedges and
@@ -26,10 +27,10 @@ export class GraphicalContinuousDynamicExpression extends AbstractGraphicalExpre
     /**
      * Create a new instance of the GraphicalContinuousDynamicExpression
      * @param continuousDynamic The continuous dynamic instruction read via ExpressionReader
-     * @param staffLine The staffline where the exoression is attached
+     * @param staffLine The staffline where the expression is attached
      */
-    constructor(continuousDynamic: ContinuousDynamicExpression, staffLine: StaffLine) {
-        super(staffLine, continuousDynamic);
+    constructor(continuousDynamic: ContinuousDynamicExpression, staffLine: StaffLine, measure: SourceMeasure) {
+        super(staffLine, continuousDynamic, measure);
 
         this.isSplittedPart = false;
         this.notToBeRemoved = false;
@@ -49,7 +50,7 @@ export class GraphicalContinuousDynamicExpression extends AbstractGraphicalExpre
     public get IsSplittedPart(): boolean { return this.isSplittedPart; }
     public set IsSplittedPart(value: boolean) { this.isSplittedPart = value; }
     /**  Is true if the dynamic is not a symbol but a text instruction. E.g. "decrescendo" */
-    public get IsVerbal(): boolean { return this.ContinuousDynamic.Label !== undefined && this.ContinuousDynamic.Label.length > 0; }
+    public get IsVerbal(): boolean { return this.ContinuousDynamic.Label && this.ContinuousDynamic.Label.length > 0; }
     /** True if this expression should not be removed if re-rendered */
     public get NotToBeRemoved(): boolean { return this.notToBeRemoved; }
     public set NotToBeRemoved(value: boolean) { this.notToBeRemoved = value; }
@@ -69,10 +70,22 @@ export class GraphicalContinuousDynamicExpression extends AbstractGraphicalExpre
         if (!this.IsVerbal && this.lines.length < 2) {
             log.warn("Not enough lines for SkyBottomLine calculation");
         }
+        if (!this.IsVerbal) {
+            if (this.ContinuousDynamic.DynamicType !== ContDynamicEnum.crescendo &&
+                this.ContinuousDynamic.DynamicType !== ContDynamicEnum.diminuendo) {
+                // for now there is only crescendo or decrescendo anyways, but this will catch errors when we add new types in the future
+                log.warn("GraphicalContinuousDynamicExpression.updateSkyBottomLine(): " +
+                    "unhandled continuous dynamic type. start measure: " + this.startMeasure?.MeasureNumber);
+            }
+        }
         switch (this.Placement) {
             case PlacementEnum.Above:
                 if (!this.IsVerbal) {
-                    skyBottomLineCalculator.updateSkyLineWithWedge(this.lines[0].Start, this.lines[0].End);
+                    if (this.ContinuousDynamic.DynamicType === ContDynamicEnum.crescendo) {
+                        skyBottomLineCalculator.updateSkyLineWithWedge(this.lines[0].Start, this.lines[0].End);
+                    } else if (this.ContinuousDynamic.DynamicType === ContDynamicEnum.diminuendo) {
+                        skyBottomLineCalculator.updateSkyLineWithWedge(this.lines[0].End, this.lines[0].Start);
+                    } // else covered with the log.warn above
                 } else {
                     const yValue: number = this.label.PositionAndShape.BorderMarginTop + this.label.PositionAndShape.RelativePosition.y;
                     skyBottomLineCalculator.updateSkyLineInRange(left, right, yValue);
@@ -80,7 +93,12 @@ export class GraphicalContinuousDynamicExpression extends AbstractGraphicalExpre
                 break;
             case PlacementEnum.Below:
                 if (!this.IsVerbal) {
-                    skyBottomLineCalculator.updateBottomLineWithWedge(this.lines[1].Start, this.lines[1].End);
+                    // console.log(`id: ${this.parentStaffLine.ParentStaff.Id}`);
+                    if (this.ContinuousDynamic.DynamicType === ContDynamicEnum.crescendo) {
+                        skyBottomLineCalculator.updateBottomLineWithWedge(this.lines[1].Start, this.lines[1].End);
+                    } else if (this.ContinuousDynamic.DynamicType === ContDynamicEnum.diminuendo) {
+                        skyBottomLineCalculator.updateBottomLineWithWedge(this.lines[1].End, this.lines[1].Start);
+                    } // else covered with the log.warn above
                 } else {
                     const yValue: number = this.label.PositionAndShape.BorderMarginBottom + this.label.PositionAndShape.RelativePosition.y;
                     skyBottomLineCalculator.updateBottomLineInRange(left, right, yValue);
@@ -247,6 +265,10 @@ export class GraphicalContinuousDynamicExpression extends AbstractGraphicalExpre
         this.PositionAndShape.RelativePosition = this.lines[0].Start;
         this.PositionAndShape.BorderMarginTop = this.lines[0].End.y - this.lines[0].Start.y;
         this.PositionAndShape.BorderMarginBottom = this.lines[1].End.y - this.lines[1].Start.y;
+        this.PositionAndShape.Center.y = (this.PositionAndShape.BorderMarginTop + this.PositionAndShape.BorderMarginBottom) / 2;
+        // TODO is the center position correct? it wasn't set before, important for AlignmentManager.alignDynamicExpressions()
+        // console.log(`relative y, center y: ${this.PositionAndShape.RelativePosition.y},${this.PositionAndShape.Center.y})`);
+
 
         if (this.ContinuousDynamic.DynamicType === ContDynamicEnum.crescendo) {
             this.PositionAndShape.BorderMarginLeft = 0;

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

@@ -33,7 +33,7 @@ export class GraphicalCurve {
      * @param relativePosition
      */
     public calculateCurvePointAtIndex(relativePosition: number): PointF2D {
-        const index: number =  Math.round(relativePosition);
+        const index: number =  Math.round(relativePosition * GraphicalCurve.bezierCurveStepSize);
         if (index < 0 || index >= GraphicalCurve.bezierCurveStepSize) {
             return new PointF2D();
         }

+ 2 - 2
src/MusicalScore/Graphical/GraphicalInstantaneousDynamicExpression.ts

@@ -4,14 +4,14 @@ import { GraphicalMeasure } from "./GraphicalMeasure";
 import { AbstractGraphicalExpression } from "./AbstractGraphicalExpression";
 import { SkyBottomLineCalculator } from "./SkyBottomLineCalculator";
 import { PlacementEnum } from "../VoiceData/Expressions/AbstractExpression";
-import * as log from "loglevel";
+import log from "loglevel";
 
 export class GraphicalInstantaneousDynamicExpression extends AbstractGraphicalExpression {
     protected mInstantaneousDynamicExpression: InstantaneousDynamicExpression;
     protected mMeasure: GraphicalMeasure;
 
     constructor(instantaneousDynamic: InstantaneousDynamicExpression, staffLine: StaffLine, measure: GraphicalMeasure) {
-        super(staffLine, instantaneousDynamic);
+        super(staffLine, instantaneousDynamic, measure.parentSourceMeasure);
         this.mInstantaneousDynamicExpression = instantaneousDynamic;
         this.mMeasure = measure;
     }

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

@@ -7,7 +7,7 @@ import { AbstractGraphicalExpression } from "./AbstractGraphicalExpression";
 export class GraphicalInstantaneousTempoExpression extends AbstractGraphicalExpression {
 
     constructor(tempoExpresssion: AbstractTempoExpression, label: GraphicalLabel) {
-        super((label.PositionAndShape.Parent.DataObject as StaffLine), tempoExpresssion);
+        super((label.PositionAndShape.Parent.DataObject as StaffLine), tempoExpresssion, tempoExpresssion.parentMeasure);
         this.label = label;
     }
 

+ 65 - 25
src/MusicalScore/Graphical/GraphicalLabel.ts

@@ -1,15 +1,17 @@
-import {Label} from "../Label";
-import {TextAlignmentEnum} from "../../Common/Enums/TextAlignment";
-import {Clickable} from "./Clickable";
-import {BoundingBox} from "./BoundingBox";
-import {EngravingRules} from "./EngravingRules";
-import {MusicSheetCalculator} from "./MusicSheetCalculator";
+import { TextAlignmentEnum } from "../../Common/Enums/TextAlignment";
+import { Label } from "../Label";
+import { BoundingBox } from "./BoundingBox";
+import { Clickable } from "./Clickable";
+import { EngravingRules } from "./EngravingRules";
+import { MusicSheetCalculator } from "./MusicSheetCalculator";
 
 /**
  * The graphical counterpart of a Label
  */
 export class GraphicalLabel extends Clickable {
     private label: Label;
+    private rules: EngravingRules;
+    public TextLines: {text: string, xOffset: number, width: number}[];
 
     /**
      * Creates a new GraphicalLabel from a Label
@@ -18,12 +20,14 @@ export class GraphicalLabel extends Clickable {
      * @param alignment Alignement like left, right, top, ...
      * @param parent Parent Bounding Box where the label is attached to
      */
-    constructor(label: Label, textHeight: number, alignment: TextAlignmentEnum, parent: BoundingBox = undefined) {
+    constructor(label: Label, textHeight: number, alignment: TextAlignmentEnum, rules: EngravingRules,
+                parent: BoundingBox = undefined, ) {
         super();
         this.label = label;
         this.boundingBox = new BoundingBox(this, parent);
         this.label.fontHeight = textHeight;
         this.label.textAlignment = alignment;
+        this.rules = rules;
     }
 
     public get Label(): Label {
@@ -31,76 +35,112 @@ export class GraphicalLabel extends Clickable {
     }
 
     public toString(): string {
-        return this.label.text;
+        return `${this.label.text} (${this.boundingBox.RelativePosition.x},${this.boundingBox.RelativePosition.y})`;
     }
 
     /**
      * Calculate GraphicalLabel's Borders according to its Alignment
+     * Create also the text-lines and their offsets here
      */
     public setLabelPositionAndShapeBorders(): void {
         if (this.Label.text.trim() === "") {
             return;
         }
-        const labelMarginBorderFactor: number = EngravingRules.Rules.LabelMarginBorderFactor;
+        this.TextLines = [];
+        const labelMarginBorderFactor: number = this.rules?.LabelMarginBorderFactor ?? 0.1;
+        const lines: string[] = this.Label.text.split(/[\n\r]+/g);
+        const numOfLines: number = lines.length;
+        let maxWidth: number = 0;
+        for (let i: number = 0; i < numOfLines; i++) {
+            const line: string = lines[i].trim();
+            const widthToHeightRatio: number =
+            MusicSheetCalculator.TextMeasurer.computeTextWidthToHeightRatio(
+               line, this.Label.font, this.Label.fontStyle, this.label.fontFamily);
+            const currWidth: number = this.Label.fontHeight * widthToHeightRatio;
+            maxWidth = Math.max(maxWidth, currWidth);
+            // here push only text and width of the text:
+            this.TextLines.push({text: line, xOffset: 0, width: currWidth});
+        }
+
+        // maxWidth is calculated ->
+        // now also set the x-offsets:
+        for (const line of this.TextLines) {
+            let xOffset: number = 0;
+            switch (this.Label.textAlignment) {
+                case TextAlignmentEnum.RightBottom:
+                case TextAlignmentEnum.RightCenter:
+                case TextAlignmentEnum.RightTop:
+                    xOffset = maxWidth - line.width;
+                    break;
+                case TextAlignmentEnum.CenterBottom:
+                case TextAlignmentEnum.CenterCenter:
+                case TextAlignmentEnum.CenterTop:
+                    xOffset = (maxWidth - line.width) / 2;
+                    break;
+                default:
+                    break;
+            }
+            line.xOffset = xOffset;
+        }
 
-        const widthToHeightRatio: number =
-            MusicSheetCalculator.TextMeasurer.computeTextWidthToHeightRatio(this.Label.text, this.Label.font, this.Label.fontStyle);
-        const height: number = this.Label.fontHeight;
-        const width: number = height * widthToHeightRatio;
+        let height: number = this.Label.fontHeight * numOfLines;
+        if (this.rules.SpacingBetweenTextLines > 0 && this.TextLines.length > 1) {
+            height += (this.rules.SpacingBetweenTextLines * numOfLines) / 10;
+        }
         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;

+ 6 - 5
src/MusicalScore/Graphical/GraphicalLyricEntry.ts

@@ -4,8 +4,7 @@ import {GraphicalLabel} from "./GraphicalLabel";
 import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
 import {Label} from "../Label";
 import {PointF2D} from "../../Common/DataObjects/PointF2D";
-import { EngravingRules } from "./EngravingRules";
-import { TextAlignmentEnum } from "../../Common/Enums/TextAlignment";
+import {TextAlignmentEnum} from "../../Common/Enums/TextAlignment";
 
 /**
  * The graphical counterpart of a [[LyricsEntry]]
@@ -19,7 +18,7 @@ export class GraphicalLyricEntry {
     constructor(lyricsEntry: LyricsEntry, graphicalStaffEntry: GraphicalStaffEntry, lyricsHeight: number, staffHeight: number) {
         this.lyricsEntry = lyricsEntry;
         this.graphicalStaffEntry = graphicalStaffEntry;
-        const lyricsTextAlignment: TextAlignmentEnum = EngravingRules.Rules.LyricsAlignmentStandard;
+        const lyricsTextAlignment: TextAlignmentEnum = graphicalStaffEntry.parentMeasure.parentSourceMeasure.Rules.LyricsAlignmentStandard;
         // for small notes with long text, use center alignment
         // TODO use this, fix center+left alignment combination spacing
         if (lyricsEntry.Text.length >= 4
@@ -27,11 +26,13 @@ export class GraphicalLyricEntry {
             && lyricsTextAlignment === TextAlignmentEnum.LeftBottom) {
             // lyricsTextAlignment = TextAlignmentAndPlacement.CenterBottom;
         }
+        const label: Label = new Label(lyricsEntry.Text);
         this.graphicalLabel = new GraphicalLabel(
-            new Label(lyricsEntry.Text),
+            label,
             lyricsHeight,
             lyricsTextAlignment,
-            graphicalStaffEntry.PositionAndShape
+            this.graphicalStaffEntry.parentMeasure.parentSourceMeasure.Rules,
+            graphicalStaffEntry.PositionAndShape,
         );
         this.graphicalLabel.PositionAndShape.RelativePosition = new PointF2D(0, staffHeight);
         if (lyricsTextAlignment === TextAlignmentEnum.LeftBottom) {

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

@@ -27,7 +27,7 @@ export class GraphicalLyricWord {
 
     public isFilled(): boolean {
         for (let i: number = 0; i < this.graphicalLyricsEntries.length; i++) {
-            if (this.graphicalLyricsEntries[i] === undefined) {
+            if (!this.graphicalLyricsEntries[i]) {
                 return false;
             }
         }

+ 23 - 10
src/MusicalScore/Graphical/GraphicalMeasure.ts

@@ -26,14 +26,14 @@ export abstract class GraphicalMeasure extends GraphicalObject {
         this.parentStaff = staff;
         this.parentSourceMeasure = parentSourceMeasure;
         this.parentStaffLine = staffLine;
-        if (staffLine !== undefined) {
+        if (staffLine) {
             this.parentStaff = staffLine.ParentStaff;
             this.PositionAndShape = new BoundingBox(this, staffLine.PositionAndShape);
         } else {
             this.PositionAndShape = new BoundingBox(this);
         }
         this.PositionAndShape.BorderBottom = 4;
-        if (this.parentSourceMeasure !== undefined) {
+        if (this.parentSourceMeasure) {
             this.measureNumber = this.parentSourceMeasure.MeasureNumber;
         }
 
@@ -42,7 +42,6 @@ export abstract class GraphicalMeasure extends GraphicalObject {
 
     public parentSourceMeasure: SourceMeasure;
     public staffEntries: GraphicalStaffEntry[];
-    public parentMusicSystem: MusicSystem;
     /**
      * The x-width of possibly existing: repetition start line, clef, key, rhythm.
      */
@@ -60,8 +59,14 @@ export abstract class GraphicalMeasure extends GraphicalObject {
      */
     public endInstructionsWidth: number;
     public hasError: boolean;
+    /**
+     * Whether or not this measure is nothing but rest(s).
+     * Also see SourceMeasure.allRests, which is not the same, because a source measure can have multiple staffs/graphicalMeasures.
+     */
+    public hasOnlyRests: boolean = false;
 
     private parentStaff: Staff;
+    private parentMusicSystem: MusicSystem;
     private measureNumber: number = -1;
     private parentStaffLine: StaffLine;
 
@@ -69,6 +74,14 @@ export abstract class GraphicalMeasure extends GraphicalObject {
         return this.parentStaff;
     }
 
+    public get ParentMusicSystem(): MusicSystem {
+        return this.parentMusicSystem;
+    }
+
+    public set ParentMusicSystem(value: MusicSystem) {
+        this.parentMusicSystem = value;
+    }
+
     public get MeasureNumber(): number {
         return this.measureNumber;
     }
@@ -95,7 +108,7 @@ export abstract class GraphicalMeasure extends GraphicalObject {
 
     public set ParentStaffLine(value: StaffLine) {
         this.parentStaffLine = value;
-        if (this.parentStaffLine !== undefined) {
+        if (this.parentStaffLine) {
             this.PositionAndShape.Parent = this.parentStaffLine.PositionAndShape;
         }
     }
@@ -182,7 +195,7 @@ export abstract class GraphicalMeasure extends GraphicalObject {
     public findGraphicalStaffEntryFromTimestamp(relativeTimestamp: Fraction): GraphicalStaffEntry {
         for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
             const graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx];
-            if (graphicalStaffEntry.relInMeasureTimestamp.Equals(relativeTimestamp)) {
+            if (graphicalStaffEntry.relInMeasureTimestamp?.Equals(relativeTimestamp)) {
                 return graphicalStaffEntry;
             }
         }
@@ -283,7 +296,7 @@ export abstract class GraphicalMeasure extends GraphicalObject {
      * @param staffEntry
      */
     public addGraphicalStaffEntryAtTimestamp(staffEntry: GraphicalStaffEntry): void {
-        if (staffEntry !== undefined) {
+        if (staffEntry) {
             if (this.staffEntries.length === 0 || this.staffEntries[this.staffEntries.length - 1].relInMeasureTimestamp.lt(staffEntry.relInMeasureTimestamp)) {
                 this.staffEntries.push(staffEntry);
             } else {
@@ -302,7 +315,7 @@ export abstract class GraphicalMeasure extends GraphicalObject {
 
     public beginsWithLineRepetition(): boolean {
         const sourceMeasure: SourceMeasure = this.parentSourceMeasure;
-        if (sourceMeasure === undefined) {
+        if (!sourceMeasure) {
             return false;
         }
         return sourceMeasure.beginsWithLineRepetition();
@@ -314,7 +327,7 @@ export abstract class GraphicalMeasure extends GraphicalObject {
      */
     public endsWithLineRepetition(): boolean {
         const sourceMeasure: SourceMeasure = this.parentSourceMeasure;
-        if (sourceMeasure === undefined) {
+        if (!sourceMeasure) {
             return false;
         }
         return sourceMeasure.endsWithLineRepetition();
@@ -326,7 +339,7 @@ export abstract class GraphicalMeasure extends GraphicalObject {
      */
     public beginsWithWordRepetition(): boolean {
         const sourceMeasure: SourceMeasure = this.parentSourceMeasure;
-        if (sourceMeasure === undefined) {
+        if (!sourceMeasure) {
             return false;
         }
         return sourceMeasure.beginsWithWordRepetition();
@@ -337,7 +350,7 @@ export abstract class GraphicalMeasure extends GraphicalObject {
      */
     public endsWithWordRepetition(): boolean {
         const sourceMeasure: SourceMeasure = this.parentSourceMeasure;
-        if (sourceMeasure === undefined) {
+        if (!sourceMeasure) {
             return false;
         }
         return sourceMeasure.endsWithWordRepetition();

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

@@ -10,6 +10,7 @@ export class GraphicalMusicPage extends GraphicalObject {
     private musicSystems: MusicSystem[] = [];
     private labels: GraphicalLabel[] = [];
     private parent: GraphicalMusicSheet;
+    private pageNumber: number;
 
     constructor(parent: GraphicalMusicSheet) {
         super();
@@ -41,6 +42,14 @@ export class GraphicalMusicPage extends GraphicalObject {
         this.parent = value;
     }
 
+    public get PageNumber(): number {
+        return this.pageNumber;
+    }
+
+    public set PageNumber(value: number) {
+        this.pageNumber = value;
+    }
+
     /**
      * This method calculates the absolute Position of each GraphicalMusicPage according to a given placement
      * @param pageIndex
@@ -48,26 +57,29 @@ export class GraphicalMusicPage extends GraphicalObject {
      * @returns {PointF2D}
      */
     public setMusicPageAbsolutePosition(pageIndex: number, rules: EngravingRules): PointF2D {
-        if (rules.PagePlacement === PagePlacementEnum.Down) {
-            return new PointF2D(0.0, pageIndex * rules.PageHeight);
-        } else if (rules.PagePlacement === PagePlacementEnum.Right) {
-            return new PointF2D(pageIndex * this.parent.ParentMusicSheet.pageWidth, 0.0);
-        } else {
-            // placement RightDown
-            if (pageIndex % 2 === 0) {
-                if (pageIndex === 0) {
-                    return new PointF2D(0.0, pageIndex * rules.PageHeight);
-                } else {
-                    return new PointF2D(0.0, (pageIndex - 1) * rules.PageHeight);
-                }
-            } else {
-                if (pageIndex === 1) {
-                    return new PointF2D(this.parent.ParentMusicSheet.pageWidth, (pageIndex - 1) * rules.PageHeight);
-                } else {
-                    return new PointF2D(this.parent.ParentMusicSheet.pageWidth, (pageIndex - 2) * rules.PageHeight);
-                }
-            }
-        }
+        return new PointF2D(0.0, 0.0);
+
+        // use this code if pages are rendered on only one canvas:
+        // if (rules.PagePlacement === PagePlacementEnum.Down) {
+        //     return new PointF2D(0.0, pageIndex * rules.PageHeight);
+        // } else if (rules.PagePlacement === PagePlacementEnum.Right) {
+        //     return new PointF2D(pageIndex * this.parent.ParentMusicSheet.pageWidth, 0.0);
+        // } else {
+        //     // placement RightDown
+        //     if (pageIndex % 2 === 0) {
+        //         if (pageIndex === 0) {
+        //             return new PointF2D(0.0, pageIndex * rules.PageHeight);
+        //         } else {
+        //             return new PointF2D(0.0, (pageIndex - 1) * rules.PageHeight);
+        //         }
+        //     } else {
+        //         if (pageIndex === 1) {
+        //             return new PointF2D(this.parent.ParentMusicSheet.pageWidth, (pageIndex - 1) * rules.PageHeight);
+        //         } else {
+        //             return new PointF2D(this.parent.ParentMusicSheet.pageWidth, (pageIndex - 2) * rules.PageHeight);
+        //         }
+        //     }
+        // }
     }
 }
 

+ 79 - 57
src/MusicalScore/Graphical/GraphicalMusicSheet.ts

@@ -17,8 +17,8 @@ import {GraphicalNote} from "./GraphicalNote";
 import {Instrument} from "../Instrument";
 import {BoundingBox} from "./BoundingBox";
 import {MusicSheetCalculator} from "./MusicSheetCalculator";
-import * as log from "loglevel";
-import Dictionary from "typescript-collections/dist/lib/Dictionary";
+import log from "loglevel";
+//import { Dictionary } from "typescript-collections"; // unused for now
 import {CollectionUtil} from "../../Util/CollectionUtil";
 import {SelectionStartSymbol} from "./SelectionStartSymbol";
 import {SelectionEndSymbol} from "./SelectionEndSymbol";
@@ -32,16 +32,14 @@ export class GraphicalMusicSheet {
         this.musicSheet = musicSheet;
         this.numberOfStaves = this.musicSheet.Staves.length;
         this.calculator = calculator;
-        this.sourceToGraphicalMeasureLinks = new Dictionary<SourceMeasure, GraphicalMeasure[]>();
         this.calculator.initialize(this);
     }
 
-    public sourceToGraphicalMeasureLinks: Dictionary<SourceMeasure, GraphicalMeasure[]>;
-
     private musicSheet: MusicSheet;
     //private fontInfo: FontInfo = FontInfo.Info;
     private calculator: MusicSheetCalculator;
     private musicPages: GraphicalMusicPage[] = [];
+    /** measures (i,j) where i is the measure number and j the staff index (e.g. staff indices 0, 1 for two piano parts) */
     private measureList: GraphicalMeasure[][] = [];
     private verticalGraphicalStaffEntryContainers: VerticalGraphicalStaffEntryContainer[] = [];
     private title: GraphicalLabel;
@@ -166,7 +164,7 @@ export class GraphicalMusicSheet {
      */
     public static transformRelativeToAbsolutePosition(graphicalMusicSheet: GraphicalMusicSheet): void {
         for (let i: number = 0; i < graphicalMusicSheet.MusicPages.length; i++) {
-            const pageAbsolute: PointF2D = graphicalMusicSheet.MusicPages[i].setMusicPageAbsolutePosition(i, graphicalMusicSheet.ParentMusicSheet.rules);
+            const pageAbsolute: PointF2D = graphicalMusicSheet.MusicPages[i].setMusicPageAbsolutePosition(i, graphicalMusicSheet.ParentMusicSheet.Rules);
             const page: GraphicalMusicPage = graphicalMusicSheet.MusicPages[i];
             page.PositionAndShape.calculateAbsolutePositionsRecursive(pageAbsolute.x, pageAbsolute.y);
         }
@@ -182,9 +180,10 @@ export class GraphicalMusicSheet {
         this.calculator.calculate();
     }
 
-    public prepare(): void {
-        this.calculator.prepareGraphicalMusicSheet();
-    }
+    // unused method
+    // public prepare(): void {
+    //     this.calculator.prepareGraphicalMusicSheet();
+    // }
 
     public EnforceRedrawOfMusicSystems(): void {
         for (let idx: number = 0, len: number = this.musicPages.length; idx < len; ++idx) {
@@ -204,9 +203,20 @@ export class GraphicalMusicSheet {
         return undefined;
     }
 
+    public findGraphicalMeasure(measureIndex: number, staffIndex: number): GraphicalMeasure {
+        for (let i: number = measureIndex; i >= 0; i--) {
+            const gMeasure: GraphicalMeasure = this.measureList[i][staffIndex];
+            if (gMeasure) {
+                return gMeasure;
+            }
+            // else look backwards (previous measures). this is only really valid for MultipleRestMeasures of course.
+        }
+        return undefined; // shouldn't happen
+    }
+
     /**
      * Search the MeasureList for a certain GraphicalStaffEntry with the given SourceStaffEntry,
-     * at a certain verticalIndex (eg a corresponnding Staff), starting at a specific horizontalIndex (eg specific GraphicalMeasure).
+     * at a certain verticalIndex (eg a corresponding Staff), starting at a specific horizontalIndex (eg specific GraphicalMeasure).
      * @param staffIndex
      * @param measureIndex
      * @param sourceStaffEntry
@@ -215,6 +225,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) {
@@ -246,7 +259,7 @@ export class GraphicalMusicSheet {
         return undefined;
     }
 
-    public getFirstVisibleMeasuresListFromIndeces(start: number, end: number): GraphicalMeasure[] {
+    public getFirstVisibleMeasuresListFromIndices(start: number, end: number): GraphicalMeasure[] {
         const graphicalMeasures: GraphicalMeasure[] = [];
         const numberOfStaves: number = this.measureList[0].length;
         for (let i: number = start; i <= end; i++) {
@@ -290,10 +303,10 @@ export class GraphicalMusicSheet {
     public initializeActiveClefs(): ClefInstruction[] {
         const activeClefs: ClefInstruction[] = [];
         const firstSourceMeasure: SourceMeasure = this.musicSheet.getFirstSourceMeasure();
-        if (firstSourceMeasure !== undefined) {
+        if (firstSourceMeasure) {
             for (let i: number = 0; i < firstSourceMeasure.CompleteNumberOfStaves; i++) {
                 let clef: ClefInstruction = new ClefInstruction();
-                if (firstSourceMeasure.FirstInstructionsStaffEntries[i] !== undefined) {
+                if (firstSourceMeasure.FirstInstructionsStaffEntries[i]) {
                     for (let idx: number = 0, len: number = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions.length; idx < len; ++idx) {
                         const abstractNotationInstruction: AbstractNotationInstruction = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions[idx];
                         if (abstractNotationInstruction instanceof ClefInstruction) {
@@ -310,7 +323,7 @@ export class GraphicalMusicSheet {
 
     public GetMainKey(): KeyInstruction {
         const firstSourceMeasure: SourceMeasure = this.musicSheet.getFirstSourceMeasure();
-        if (firstSourceMeasure !== undefined) {
+        if (firstSourceMeasure) {
             for (let i: number = 0; i < firstSourceMeasure.CompleteNumberOfStaves; i++) {
                 for (let idx: number = 0, len: number = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions.length; idx < len; ++idx) {
                     const abstractNotationInstruction: AbstractNotationInstruction = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions[idx];
@@ -421,14 +434,14 @@ export class GraphicalMusicSheet {
     }
 
     /**
-     * Get a List with the indeces of all the visible GraphicalMeasures and calculates their
+     * Get a List with the indices of all the visible GraphicalMeasures and calculates their
      * corresponding indices in the first SourceMeasure, taking into account Instruments with multiple Staves.
      * @param visibleMeasures
      * @returns {number[]}
      */
-    public getVisibleStavesIndecesFromSourceMeasure(visibleMeasures: GraphicalMeasure[]): number[] {
+    public getVisibleStavesIndicesFromSourceMeasure(visibleMeasures: GraphicalMeasure[]): number[] {
         const visibleInstruments: Instrument[] = [];
-        const visibleStavesIndeces: number[] = [];
+        const visibleStavesIndices: number[] = [];
         for (let idx: number = 0, len: number = visibleMeasures.length; idx < len; ++idx) {
             const graphicalMeasure: GraphicalMeasure = visibleMeasures[idx];
             const instrument: Instrument = graphicalMeasure.ParentStaff.ParentInstrument;
@@ -440,27 +453,35 @@ export class GraphicalMusicSheet {
             const instrument: Instrument = visibleInstruments[idx];
             const index: number = this.musicSheet.getGlobalStaffIndexOfFirstStaff(instrument);
             for (let j: number = 0; j < instrument.Staves.length; j++) {
-                visibleStavesIndeces.push(index + j);
+                visibleStavesIndices.push(index + j);
             }
         }
-        return visibleStavesIndeces;
+        return visibleStavesIndices;
     }
 
     /**
-     * Returns the GraphicalMeasure with the given SourceMeasure as Parent at the given Index.
+     * Returns the GraphicalMeasure with the given SourceMeasure as Parent at the given staff index.
      * @param sourceMeasure
-     * @param index
+     * @param staffIndex
      * @returns {any}
      */
-    public getGraphicalMeasureFromSourceMeasureAndIndex(sourceMeasure: SourceMeasure, index: number): GraphicalMeasure {
+    public getGraphicalMeasureFromSourceMeasureAndIndex(sourceMeasure: SourceMeasure, staffIndex: number): GraphicalMeasure {
         for (let i: number = 0; i < this.measureList.length; i++) {
-            if (this.measureList[i][0].parentSourceMeasure === sourceMeasure) {
-                return this.measureList[i][index];
+            if (this.measureList[i][0]?.parentSourceMeasure === sourceMeasure) {
+                return this.measureList[i][staffIndex];
             }
         }
         return undefined;
     }
 
+    public getLastGraphicalMeasureFromIndex(staffIndex: number, lastRendered: boolean = true): GraphicalMeasure {
+        let measureIndex: number = this.measureList.length - 1;
+        if (lastRendered) {
+            measureIndex = Math.min(measureIndex, this.musicSheet.Rules.MaxMeasureToDrawIndex);
+        }
+        return this.measureList[measureIndex][staffIndex];
+    }
+
     public getMeasureIndex(graphicalMeasure: GraphicalMeasure, measureIndex: number, inListIndex: number): boolean {
         measureIndex = 0;
         inListIndex = 0;
@@ -475,7 +496,7 @@ export class GraphicalMusicSheet {
         return false;
     }
 
-    public GetNearesNote(clickPosition: PointF2D, maxClickDist: PointF2D): GraphicalNote {
+    public GetNearestNote(clickPosition: PointF2D, maxClickDist: PointF2D): GraphicalNote {
         const initialSearchArea: number = 10;
         const foundNotes: GraphicalNote[] = [];
 
@@ -492,7 +513,7 @@ export class GraphicalMusicSheet {
             const graphicalMusicPage: GraphicalMusicPage = this.MusicPages[idx];
             const entries: GraphicalNote[] = graphicalMusicPage.PositionAndShape.getObjectsInRegion<GraphicalNote>(region);
             //let entriesArr: GraphicalNote[] = __as__<GraphicalNote[]>(entries, GraphicalNote[]) ? ? entries;
-            if (entries === undefined) {
+            if (!entries) {
                 continue;
             } else {
                 for (let idx2: number = 0, len2: number = entries.length; idx2 < len2; ++idx2) {
@@ -512,7 +533,7 @@ export class GraphicalMusicSheet {
             if (closest === undefined) {
                 closest = note;
             } else {
-                if (note.parentVoiceEntry.parentStaffEntry.relInMeasureTimestamp === undefined) {
+                if (!note.parentVoiceEntry.parentStaffEntry.relInMeasureTimestamp) {
                     continue;
                 }
                 const deltaNew: number = this.CalculateDistance(note.PositionAndShape.AbsolutePosition, clickPosition);
@@ -522,7 +543,7 @@ export class GraphicalMusicSheet {
                 }
             }
         }
-        if (closest !== undefined) {
+        if (closest) {
             return closest;
         }
         // TODO No staff entry was found. Feedback?
@@ -569,7 +590,7 @@ export class GraphicalMusicSheet {
         for (let idx: number = 0, len: number = this.MusicPages.length; idx < len; ++idx) {
             const graphicalMusicPage: GraphicalMusicPage = this.MusicPages[idx];
             const entries: GraphicalStaffEntry[] = graphicalMusicPage.PositionAndShape.getObjectsInRegion<GraphicalStaffEntry>(region, false);
-            if (entries === undefined || entries.length === 0) {
+            if (!entries || entries.length === 0) {
                 continue;
             } else {
                 for (let idx2: number = 0, len2: number = entries.length; idx2 < len2; ++idx2) {
@@ -585,7 +606,7 @@ export class GraphicalMusicSheet {
             if (closest === undefined) {
                 closest = gse;
             } else {
-                if (gse.relInMeasureTimestamp === undefined) {
+                if (!gse.relInMeasureTimestamp) {
                     continue;
                 }
                 const deltaNew: number = this.CalculateDistance(gse.PositionAndShape.AbsolutePosition, clickPosition);
@@ -595,7 +616,7 @@ export class GraphicalMusicSheet {
                 }
             }
         }
-        if (closest !== undefined) {
+        if (closest) {
             return closest;
         }
         // TODO No staff entry was found. Feedback?
@@ -605,7 +626,7 @@ export class GraphicalMusicSheet {
 
     public GetPossibleCommentAnchor(clickPosition: PointF2D): SourceStaffEntry {
         const entry: GraphicalStaffEntry = this.GetNearestStaffEntry(clickPosition);
-        if (entry === undefined) {
+        if (!entry) {
             return undefined;
         }
         return entry.sourceStaffEntry;
@@ -615,7 +636,7 @@ export class GraphicalMusicSheet {
         for (let idx: number = 0, len: number = this.musicPages.length; idx < len; ++idx) {
             const page: GraphicalMusicPage = this.musicPages[idx];
             const o: Object = page.PositionAndShape.getClickedObjectOfType<T>(positionOnMusicSheet);
-            if (o !== undefined) {
+            if (o) {
                 return (o as T);
             }
         }
@@ -624,7 +645,7 @@ export class GraphicalMusicSheet {
 
     public tryGetTimestampFromPosition(positionOnMusicSheet: PointF2D): Fraction {
         const entry: GraphicalStaffEntry = this.getClickedObjectOfType<GraphicalStaffEntry>(positionOnMusicSheet);
-        if (entry === undefined) {
+        if (!entry) {
             return undefined;
         }
         return entry.getAbsoluteTimestamp();
@@ -643,7 +664,7 @@ export class GraphicalMusicSheet {
     public tryGetTimeStampFromPosition(positionOnMusicSheet: PointF2D): Fraction {
         try {
             const entry: GraphicalStaffEntry = this.GetNearestStaffEntry(positionOnMusicSheet);
-            if (entry === undefined) {
+            if (!entry) {
                 return undefined;
             }
             return entry.getAbsoluteTimestamp();
@@ -668,12 +689,12 @@ export class GraphicalMusicSheet {
         try {
             for (let idx: number = 0, len: number = container.StaffEntries.length; idx < len; ++idx) {
                 const entry: GraphicalStaffEntry = container.StaffEntries[idx];
-                if (entry === undefined || !entry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
+                if (!entry || !entry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
                     continue;
                 }
-                if (staffEntry === undefined) {
+                if (!staffEntry) {
                     staffEntry = entry;
-                } else if (entry.PositionAndShape !== undefined && staffEntry.PositionAndShape !== undefined) {
+                } else if (entry.PositionAndShape && staffEntry.PositionAndShape) {
                     if (staffEntry.PositionAndShape.RelativePosition.x > entry.PositionAndShape.RelativePosition.x) {
                         staffEntry = entry;
                     }
@@ -697,7 +718,7 @@ export class GraphicalMusicSheet {
             const entries: GraphicalStaffEntry[] = this.verticalGraphicalStaffEntryContainers[i].StaffEntries;
             for (let idx: number = 0, len: number = entries.length; idx < len; ++idx) {
                 const entry: GraphicalStaffEntry = entries[idx];
-                if (entry !== undefined && entry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
+                if (entry && entry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
                     return i;
                 }
             }
@@ -716,7 +737,7 @@ export class GraphicalMusicSheet {
             const entries: GraphicalStaffEntry[] = this.verticalGraphicalStaffEntryContainers[i].StaffEntries;
             for (let idx: number = 0, len: number = entries.length; idx < len; ++idx) {
                 const entry: GraphicalStaffEntry = entries[idx];
-                if (entry !== undefined && entry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
+                if (entry && entry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
                     return i;
                 }
             }
@@ -730,7 +751,7 @@ export class GraphicalMusicSheet {
         leftIndex = Math.min(this.VerticalGraphicalStaffEntryContainers.length - 1, leftIndex);
         for (let i: number = leftIndex; i >= 0; i--) {
             foundEntry = this.getStaffEntry(i);
-            if (foundEntry !== undefined) {
+            if (foundEntry) {
                 if (searchOnlyVisibleEntries) {
                     if (foundEntry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
                         return foundEntry;
@@ -748,7 +769,7 @@ export class GraphicalMusicSheet {
         const rightIndex: number = Math.max(0, Math.ceil(fractionalIndex));
         for (let i: number = rightIndex; i < this.VerticalGraphicalStaffEntryContainers.length; i++) {
             foundEntry = this.getStaffEntry(i);
-            if (foundEntry !== undefined) {
+            if (foundEntry) {
                 if (returnOnlyVisibleEntries) {
                     if (foundEntry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
                         return foundEntry;
@@ -765,7 +786,7 @@ export class GraphicalMusicSheet {
         const result: [number, MusicSystem] = this.calculateXPositionFromTimestamp(musicTimestamp);
         const xPos: number = result[0];
         const correspondingMusicSystem: MusicSystem = result[1];
-        if (correspondingMusicSystem === undefined || correspondingMusicSystem.StaffLines.length === 0) {
+        if (!correspondingMusicSystem || correspondingMusicSystem.StaffLines.length === 0) {
             return undefined;
         }
         const yCoordinate: number = correspondingMusicSystem.PositionAndShape.AbsolutePosition.y;
@@ -779,30 +800,31 @@ export class GraphicalMusicSheet {
         const previousStaffEntry: GraphicalStaffEntry = this.findClosestLeftStaffEntry(fractionalIndex, true);
         const nextStaffEntry: GraphicalStaffEntry = this.findClosestRightStaffEntry(fractionalIndex, true);
         const currentTimeStamp: number = timeStamp.RealValue;
-        if (previousStaffEntry === undefined && nextStaffEntry === undefined) {
+        if (!previousStaffEntry && !nextStaffEntry) {
             return [0, undefined];
         }
         let previousStaffEntryMusicSystem: MusicSystem = undefined;
-        if (previousStaffEntry !== undefined) {
-            previousStaffEntryMusicSystem = previousStaffEntry.parentMeasure.ParentStaffLine.ParentMusicSystem;
+        if (previousStaffEntry) {
+            // TODO sometimes one of these ParentStaffLine is undefined, either fix this or handle it here
+            previousStaffEntryMusicSystem = previousStaffEntry.parentMeasure.ParentStaffLine?.ParentMusicSystem;
         } else {
-            previousStaffEntryMusicSystem = nextStaffEntry.parentMeasure.ParentStaffLine.ParentMusicSystem;
+            previousStaffEntryMusicSystem = nextStaffEntry.parentMeasure.ParentStaffLine?.ParentMusicSystem;
         }
         let nextStaffEntryMusicSystem: MusicSystem = undefined;
-        if (nextStaffEntry !== undefined) {
-            nextStaffEntryMusicSystem = nextStaffEntry.parentMeasure.ParentStaffLine.ParentMusicSystem;
+        if (nextStaffEntry) {
+            nextStaffEntryMusicSystem = nextStaffEntry.parentMeasure.ParentStaffLine?.ParentMusicSystem;
         } else {
-            nextStaffEntryMusicSystem = previousStaffEntry.parentMeasure.ParentStaffLine.ParentMusicSystem;
+            nextStaffEntryMusicSystem = previousStaffEntry.parentMeasure.ParentStaffLine?.ParentMusicSystem;
         }
         if (previousStaffEntryMusicSystem === nextStaffEntryMusicSystem) {
             currentMusicSystem = previousStaffEntryMusicSystem;
             let fraction: number;
             let previousStaffEntryPositionX: number;
             let nextStaffEntryPositionX: number;
-            if (previousStaffEntry === undefined) {
+            if (!previousStaffEntry) {
                 previousStaffEntryPositionX = nextStaffEntryPositionX = nextStaffEntry.PositionAndShape.AbsolutePosition.x;
                 fraction = 0;
-            } else if (nextStaffEntry === undefined) {
+            } else if (!nextStaffEntry) {
                 previousStaffEntryPositionX = previousStaffEntry.PositionAndShape.AbsolutePosition.x;
                 nextStaffEntryPositionX = currentMusicSystem.GetRightBorderAbsoluteXPosition();
                 const sm: SourceMeasure = previousStaffEntry.parentMeasure.parentSourceMeasure;
@@ -868,12 +890,12 @@ export class GraphicalMusicSheet {
         return followedInstrumentCount;
     }
 
-    public GetGraphicalFromSourceMeasure(sourceMeasure: SourceMeasure): GraphicalMeasure[] {
-        return this.sourceToGraphicalMeasureLinks.getValue(sourceMeasure);
-    }
+    /*public GetGraphicalFromSourceMeasure(sourceMeasure: SourceMeasure): GraphicalMeasure[] {
+        return this.sourceToGraphicalMeasureLinks.getValue(sourceMeasure); // TODO gets wrong measure because sourceMeasure is not a valid key
+    }*/
 
     public GetGraphicalFromSourceStaffEntry(sourceStaffEntry: SourceStaffEntry): GraphicalStaffEntry {
-        const graphicalMeasure: GraphicalMeasure = this.GetGraphicalFromSourceMeasure(sourceStaffEntry.VerticalContainerParent.ParentMeasure)
+        const graphicalMeasure: GraphicalMeasure = sourceStaffEntry.VerticalContainerParent.ParentMeasure.VerticalMeasureList
             [sourceStaffEntry.ParentStaff.idInMusicSheet];
         return graphicalMeasure.findGraphicalStaffEntryFromTimestamp(sourceStaffEntry.Timestamp);
     }
@@ -892,7 +914,7 @@ export class GraphicalMusicSheet {
     private getLongestStaffEntryDuration(index: number): Fraction {
         let maxLength: Fraction = new Fraction(0, 1);
         for (const graphicalStaffEntry of this.verticalGraphicalStaffEntryContainers[index].StaffEntries) {
-            if (graphicalStaffEntry === undefined) {
+            if (!graphicalStaffEntry) {
                 continue;
             }
             const maxLengthInStaffEntry: Fraction = graphicalStaffEntry.findStaffEntryMaxNoteLength();

+ 8 - 3
src/MusicalScore/Graphical/GraphicalNote.ts

@@ -8,6 +8,7 @@ import {GraphicalObject} from "./GraphicalObject";
 import {MusicSheetCalculator} from "./MusicSheetCalculator";
 import {BoundingBox} from "./BoundingBox";
 import {GraphicalVoiceEntry} from "./GraphicalVoiceEntry";
+import {GraphicalMusicPage} from "./GraphicalMusicPage";
 
 /**
  * The graphical counterpart of a [[Note]]
@@ -18,7 +19,7 @@ export class GraphicalNote extends GraphicalObject {
         this.sourceNote = note;
         this.parentVoiceEntry = parent;
         this.PositionAndShape = new BoundingBox(this, parent.PositionAndShape);
-        if (graphicalNoteLength !== undefined) {
+        if (graphicalNoteLength) {
             this.graphicalNoteLength = graphicalNoteLength;
         } else {
             this.graphicalNoteLength = note.Length;
@@ -34,7 +35,7 @@ export class GraphicalNote extends GraphicalObject {
 
     public Transpose(keyInstruction: KeyInstruction, activeClef: ClefInstruction, halfTones: number, octaveEnum: OctaveEnum): Pitch {
         let transposedPitch: Pitch = this.sourceNote.Pitch;
-        if (MusicSheetCalculator.transposeCalculator !== undefined) {
+        if (MusicSheetCalculator.transposeCalculator) {
             transposedPitch = MusicSheetCalculator.transposeCalculator.transposePitch(this.sourceNote.Pitch, keyInstruction, halfTones);
         }
         return transposedPitch;
@@ -49,7 +50,7 @@ export class GraphicalNote extends GraphicalObject {
       let num: number = 1;
       let product: number = 2;
       const expandedNumerator: number = fraction.GetExpandedNumerator();
-      if (this.sourceNote === undefined || this.sourceNote.NoteTuplet === undefined) {
+      if (!this.sourceNote || !this.sourceNote.NoteTuplet) {
         while (product < expandedNumerator) {
           num++;
           product = Math.pow(2, num);
@@ -57,4 +58,8 @@ export class GraphicalNote extends GraphicalObject {
       }
       return Math.min(3, num - 1);
     }
+
+    public get ParentMusicPage(): GraphicalMusicPage {
+      return this.parentVoiceEntry.parentStaffEntry.parentMeasure.ParentMusicSystem.Parent;
+    }
 }

+ 230 - 149
src/MusicalScore/Graphical/GraphicalSlur.ts

@@ -17,9 +17,10 @@ import { StemDirectionType } from "../VoiceData/VoiceEntry";
 export class GraphicalSlur extends GraphicalCurve {
     // private intersection: PointF2D;
 
-    constructor(slur: Slur) {
+    constructor(slur: Slur, rules: EngravingRules) {
         super();
         this.slur = slur;
+        this.rules = rules;
     }
 
     public slur: Slur;
@@ -27,6 +28,7 @@ export class GraphicalSlur extends GraphicalCurve {
     public placement: PlacementEnum;
     public graceStart: boolean;
     public graceEnd: boolean;
+    private rules: EngravingRules;
 
     /**
      * Compares the timespan of two Graphical Slurs
@@ -34,6 +36,11 @@ export class GraphicalSlur extends GraphicalCurve {
      * @param y
      */
     public static Compare (x: GraphicalSlur, y: GraphicalSlur ): number {
+        if (x.staffEntries.length < 1) { // x.staffEntries[i] can return undefined in Beethoven Moonlight Sonata sample
+            return -1;
+        } else if (y.staffEntries.length < 1) {
+            return 1;
+        }
         const xTimestampSpan: Fraction = Fraction.minus(x.staffEntries[x.staffEntries.length - 1].getAbsoluteTimestamp(),
                                                         x.staffEntries[0].getAbsoluteTimestamp());
         const yTimestampSpan: Fraction = Fraction.minus(y.staffEntries[y.staffEntries.length - 1].getAbsoluteTimestamp(),
@@ -63,14 +70,14 @@ export class GraphicalSlur extends GraphicalCurve {
 
         // where the Slur (not the graphicalObject) starts and ends (could belong to another StaffLine)
         let slurStartNote: GraphicalNote = startStaffEntry.findGraphicalNoteFromNote(this.slur.StartNote);
-        if (slurStartNote === undefined && this.graceStart) {
+        if (!slurStartNote && this.graceStart) {
             slurStartNote = startStaffEntry.findGraphicalNoteFromGraceNote(this.slur.StartNote);
         }
-        if (slurStartNote === undefined) {
+        if (!slurStartNote) {
             slurStartNote = startStaffEntry.findEndTieGraphicalNoteFromNoteWithStartingSlur(this.slur.StartNote, this.slur);
         }
         let slurEndNote: GraphicalNote = endStaffEntry.findGraphicalNoteFromNote(this.slur.EndNote);
-        if (slurEndNote === undefined && this.graceEnd) {
+        if (!slurEndNote && this.graceEnd) {
             slurEndNote = endStaffEntry.findGraphicalNoteFromGraceNote(this.slur.EndNote);
         }
 
@@ -89,18 +96,15 @@ export class GraphicalSlur extends GraphicalCurve {
         let endY: number = startEndPoints.endY;
         const minAngle: number = rules.SlurTangentMinAngle;
         const maxAngle: number = rules.SlurTangentMaxAngle;
-        let start: PointF2D, end: PointF2D;
         let points: PointF2D[];
 
         if (this.placement === PlacementEnum.Above) {
             startY -= rules.SlurNoteHeadYOffset;
             endY -= rules.SlurNoteHeadYOffset;
-            start = new PointF2D(startX, startY);
-            end = new PointF2D(endX, endY);
             const startUpperRight: PointF2D = new PointF2D(this.staffEntries[0].parentMeasure.PositionAndShape.RelativePosition.x
                                                            + this.staffEntries[0].PositionAndShape.RelativePosition.x,
                                                            startY);
-            if (slurStartNote !== undefined) {
+            if (slurStartNote) {
                     startUpperRight.x += this.staffEntries[0].PositionAndShape.BorderRight;
             } else  {
                     // continuing Slur from previous StaffLine - must start after last Instruction of first Measure
@@ -115,7 +119,7 @@ export class GraphicalSlur extends GraphicalCurve {
             const endUpperLeft: PointF2D = new PointF2D(this.staffEntries[this.staffEntries.length - 1].parentMeasure.PositionAndShape.RelativePosition.x
                                                         + this.staffEntries[this.staffEntries.length - 1].PositionAndShape.RelativePosition.x,
                                                         endY);
-            if (slurEndNote !== undefined) {
+            if (slurEndNote) {
                     endUpperLeft.x += this.staffEntries[this.staffEntries.length - 1].PositionAndShape.BorderLeft;
             } else {
                     // Slur continues to next StaffLine - must reach the end of current StaffLine
@@ -156,48 +160,80 @@ export class GraphicalSlur extends GraphicalCurve {
 
             // calculate tangent Lines maximum Slopes between StartPoint and EndPoint to all Points in SkyLine
                 // and tangent Lines characteristica
-            const leftLineSlope: number = this.calculateMaxLeftSlope(transformedPoints, start2, end2);
-            const rightLineSlope: number = this.calculateMaxRightSlope(transformedPoints, start2, end2);
-            const leftLineD: number = start2.y - start2.x * leftLineSlope;
-            const rightLineD: number = end2.y - end2.x * rightLineSlope;
+            const startLineSlope: number = this.calculateMaxLeftSlope(transformedPoints, start2, end2);
+            const endLineSlope: number = this.calculateMaxRightSlope(transformedPoints, start2, end2);
+            const startLineD: number = start2.y - start2.x * startLineSlope;
+            const endLineD: number = end2.y - end2.x * endLineSlope;
 
             // calculate IntersectionPoint of the 2 Lines
                 // if same Slope, then Point.X between Start and End and Point.Y fixed
             const intersectionPoint: PointF2D = new PointF2D();
             let sameSlope: boolean = false;
-            if (Math.abs(Math.abs(leftLineSlope) - Math.abs(rightLineSlope)) < 0.0001) {
+            if (Math.abs(Math.abs(startLineSlope) - Math.abs(endLineSlope)) < 0.0001) {
                 intersectionPoint.x = end2.x / 2;
                 intersectionPoint.y = 0;
                 sameSlope = true;
             } else {
-                intersectionPoint.x = (rightLineD - leftLineD) / (leftLineSlope - rightLineSlope);
-                intersectionPoint.y = leftLineSlope * intersectionPoint.x + leftLineD;
+                intersectionPoint.x = (endLineD - startLineD) / (startLineSlope - endLineSlope);
+                intersectionPoint.y = startLineSlope * intersectionPoint.x + startLineD;
             }
 
+            // calculate HeightWidthRatio between the MaxYpoint (from the points between StartPoint and EndPoint)
+            // and the X-distance from StartPoint to EndPoint
+            const heightWidthRatio: number = this.calculateHeightWidthRatio(end2.x, transformedPoints);
+
+            // Shift start- or endPoint and corresponding controlPoint away from note, if needed:
+            // e.g. if there is a close object creating a high slope, better shift it away to reduce the slope:
+            // idea is to compare the half heightWidthRatio of the bounding box of the skyline points with the slope (which is also a ratio: k/1)
+            // if the slope is greater than the half heightWidthRatio (which will 99% be the case),
+            // then add a y-offset to reduce the slope to the same value as the half heightWidthRatio of the bounding box
+            const startYOffset: number = 0;
+            const endYOffset: number = 0;
+            /*if (Math.abs(heightWidthRatio) > 0.001) {
+                // 1. start side:
+                const startSlopeRatio: number = Math.abs(startLineSlope / (heightWidthRatio * 2));
+                const maxLeftYOffset: number = Math.abs(startLineSlope);
+                startYOffset = Math.max(0, maxLeftYOffset * (Math.min(10, startSlopeRatio - 1) / 10));
+                // slope has to be adapted now due to the y-offset:
+                startLineSlope -= startYOffset;
+
+                // 2. end side:
+                const endSlopeRatio: number = Math.abs(endLineSlope / (heightWidthRatio * 2));
+                const maxRightYOffset: number = Math.abs(endLineSlope);
+                endYOffset = Math.max(0, maxRightYOffset * (Math.min(10, endSlopeRatio - 1) / 10));
+                // slope has to be adapted now due to the y-offset:
+                endLineSlope += endYOffset;
+            }*/
+
+
+
             // calculate tangent Lines Angles
                 // (using the calculated Slopes and the Ratio from the IntersectionPoint's distance to the MaxPoint in the SkyLine)
-            const leftAngle: number = minAngle;
-            const rightAngle: number = -minAngle;
-            // if the calculated Slopes (left and right) are equal, then Angles have fixed values
+            let startAngle: number = minAngle;
+            let endAngle: number = -minAngle;
+            // if the calculated Slopes (start and end) are equal, then Angles have fixed values
             if (!sameSlope) {
-                this.calculateAngles(leftAngle, rightAngle, leftLineSlope, rightLineSlope, maxAngle);
+                const result: {startAngle: number, endAngle: number} =
+                    this.calculateAngles(minAngle, startLineSlope, endLineSlope, maxAngle);
+                startAngle = result.startAngle;
+                endAngle = result.endAngle;
             }
 
             // calculate Curve's Control Points
-            const controlPoints: {leftControlPoint: PointF2D, rightControlPoint: PointF2D} =
-                this.calculateControlPoints(end2.x, leftAngle, rightAngle, transformedPoints);
+            const controlPoints: {startControlPoint: PointF2D, endControlPoint: PointF2D} =
+                this.calculateControlPoints(end2.x, startAngle, endAngle, transformedPoints, heightWidthRatio);
 
-            let leftControlPoint: PointF2D = controlPoints.leftControlPoint;
-            let rightControlPoint: PointF2D = controlPoints.rightControlPoint;
+            let startControlPoint: PointF2D = controlPoints.startControlPoint;
+            let endControlPoint: PointF2D = controlPoints.endControlPoint;
 
             // transform ControlPoints to original Coordinate System
                 // (rotate back and translate back)
-            leftControlPoint = transposeMatrix.vectorMultiplication(leftControlPoint);
-            leftControlPoint.x += startX;
-            leftControlPoint.y = -leftControlPoint.y + startY;
-            rightControlPoint = transposeMatrix.vectorMultiplication(rightControlPoint);
-            rightControlPoint.x += startX;
-            rightControlPoint.y = -rightControlPoint.y + startY;
+            startControlPoint = transposeMatrix.vectorMultiplication(startControlPoint);
+            startControlPoint.x += startX;
+            startControlPoint.y = -startControlPoint.y + startY;
+            endControlPoint = transposeMatrix.vectorMultiplication(endControlPoint);
+            endControlPoint.x += startX;
+            endControlPoint.y = -endControlPoint.y + startY;
 
             /* for DEBUG only */
             // this.intersection = transposeMatrix.vectorMultiplication(intersectionPoint);
@@ -206,12 +242,12 @@ export class GraphicalSlur extends GraphicalCurve {
             /* for DEBUG only */
 
             // set private members
-            this.bezierStartPt = start;
-            this.bezierStartControlPt = leftControlPoint;
-            this.bezierEndControlPt = rightControlPoint;
-            this.bezierEndPt = end;
+            this.bezierStartPt = new PointF2D(startX, startY - startYOffset);
+            this.bezierStartControlPt = new PointF2D(startControlPoint.x, startControlPoint.y - startYOffset);
+            this.bezierEndControlPt = new PointF2D(endControlPoint.x, endControlPoint.y - endYOffset);
+            this.bezierEndPt = new PointF2D(endX, endY - endYOffset);
 
-            // calculate CurvePoints
+            // calculate slur Curvepoints and update Skyline
             const length: number = staffLine.SkyLine.length;
             const startIndex: number = skyBottomLineCalculator.getLeftIndexForPointX(this.bezierStartPt.x, length);
             const endIndex: number = skyBottomLineCalculator.getLeftIndexForPointX(this.bezierEndPt.x, length);
@@ -236,14 +272,12 @@ export class GraphicalSlur extends GraphicalCurve {
         } else {
             startY += rules.SlurNoteHeadYOffset;
             endY += rules.SlurNoteHeadYOffset;
-            start = new PointF2D(startX, startY);
-            end = new PointF2D(endX, endY);
 
             // firstStaffEntry startLowerRightPoint and lastStaffentry endLowerLeftPoint
             const startLowerRight: PointF2D = new PointF2D(this.staffEntries[0].parentMeasure.PositionAndShape.RelativePosition.x
                                                            + this.staffEntries[0].PositionAndShape.RelativePosition.x,
                                                            startY);
-            if (slurStartNote !== undefined) {
+            if (slurStartNote) {
                 startLowerRight.x += this.staffEntries[0].PositionAndShape.BorderRight;
             } else {
                 // continuing Slur from previous StaffLine - must start after last Instruction of first Measure
@@ -257,7 +291,7 @@ export class GraphicalSlur extends GraphicalCurve {
             const endLowerLeft: PointF2D = new PointF2D(this.staffEntries[this.staffEntries.length - 1].parentMeasure.PositionAndShape.RelativePosition.x
                                                         + this.staffEntries[this.staffEntries.length - 1].PositionAndShape.RelativePosition.x,
                                                         endY);
-            if (slurEndNote !== undefined) {
+            if (slurEndNote) {
                 endLowerLeft.x += this.staffEntries[this.staffEntries.length - 1].PositionAndShape.BorderLeft;
             } else {
                 // Slur continues to next StaffLine - must reach the end of current StaffLine
@@ -297,53 +331,82 @@ export class GraphicalSlur extends GraphicalCurve {
 
             // calculate tangent Lines maximum Slopes between StartPoint and EndPoint to all Points in BottomLine
             // and tangent Lines characteristica
-            const leftLineSlope: number = this.calculateMaxLeftSlope(transformedPoints, start2, end2);
-            const rightLineSlope: number = this.calculateMaxRightSlope(transformedPoints, start2, end2);
-            const leftLineD: number = start2.y - start2.x * leftLineSlope;
-            const rightLineD: number = end2.y - end2.x * rightLineSlope;
+            const startLineSlope: number = this.calculateMaxLeftSlope(transformedPoints, start2, end2);
+            const endLineSlope: number = this.calculateMaxRightSlope(transformedPoints, start2, end2);
+            const startLineD: number = start2.y - start2.x * startLineSlope;
+            const endLineD: number = end2.y - end2.x * endLineSlope;
 
             // calculate IntersectionPoint of the 2 Lines
             // if same Slope, then Point.X between Start and End and Point.Y fixed
             const intersectionPoint: PointF2D = new PointF2D();
             let sameSlope: boolean = false;
-            if (Math.abs(Math.abs(leftLineSlope) - Math.abs(rightLineSlope)) < 0.0001) {
+            if (Math.abs(Math.abs(startLineSlope) - Math.abs(endLineSlope)) < 0.0001) {
                 intersectionPoint.x = end2.x / 2;
                 intersectionPoint.y = 0;
                 sameSlope = true;
             } else {
-                intersectionPoint.x = (rightLineD - leftLineD) / (leftLineSlope - rightLineSlope);
-                intersectionPoint.y = leftLineSlope * intersectionPoint.x + leftLineD;
+                intersectionPoint.x = (endLineD - startLineD) / (startLineSlope - endLineSlope);
+                intersectionPoint.y = startLineSlope * intersectionPoint.x + startLineD;
             }
 
+            // calculate HeightWidthRatio between the MaxYpoint (from the points between StartPoint and EndPoint)
+            // and the X-distance from StartPoint to EndPoint
+            const heightWidthRatio: number = this.calculateHeightWidthRatio(end2.x, transformedPoints);
+
+            // Shift start- or endPoint and corresponding controlPoint away from note, if needed:
+            // e.g. if there is a close object creating a high slope, better shift it away to reduce the slope:
+            // idea is to compare the half heightWidthRatio of the bounding box of the skyline points with the slope (which is also a ratio: k/1)
+            // if the slope is greater than the half heightWidthRatio (which will 99% be the case),
+            // then add a y-offset to reduce the slope to the same value as the half heightWidthRatio of the bounding box
+            const startYOffset: number = 0;
+            const endYOffset: number = 0;
+            /*if (Math.abs(heightWidthRatio) > 0.001) {
+                // 1. start side:
+                const startSlopeRatio: number = Math.abs(startLineSlope / (heightWidthRatio * 2));
+                const maxLeftYOffset: number = Math.abs(startLineSlope);
+                startYOffset = Math.max(0, maxLeftYOffset * (Math.min(10, startSlopeRatio - 1) / 10));
+                // slope has to be adapted now due to the y-offset:
+                startLineSlope -= startYOffset;
+                // 2. end side:
+                const endSlopeRatio: number = Math.abs(endLineSlope / (heightWidthRatio * 2));
+                const maxRightYOffset: number = Math.abs(endLineSlope);
+                endYOffset = Math.max(0, maxRightYOffset * (Math.min(10, endSlopeRatio - 1) / 10));
+                // slope has to be adapted now due to the y-offset:
+                endLineSlope += endYOffset;
+            } */
+
             // calculate tangent Lines Angles
             // (using the calculated Slopes and the Ratio from the IntersectionPoint's distance to the MaxPoint in the SkyLine)
-            const leftAngle: number = minAngle;
-            const rightAngle: number = -minAngle;
-            // if the calculated Slopes (left and right) are equal, then Angles have fixed values
+            let startAngle: number = minAngle;
+            let endAngle: number = -minAngle;
+            // if the calculated Slopes (start and end) are equal, then Angles have fixed values
             if (!sameSlope) {
-                this.calculateAngles(leftAngle, rightAngle, leftLineSlope, rightLineSlope, maxAngle);
+                const result: {startAngle: number, endAngle: number} =
+                    this.calculateAngles(minAngle, startLineSlope, endLineSlope, maxAngle);
+                startAngle = result.startAngle;
+                endAngle = result.endAngle;
             }
 
             // calculate Curve's Control Points
-            const controlPoints: {leftControlPoint: PointF2D, rightControlPoint: PointF2D} =
-                this.calculateControlPoints(end2.x, leftAngle, rightAngle, transformedPoints);
-            let leftControlPoint: PointF2D = controlPoints.leftControlPoint;
-            let rightControlPoint: PointF2D = controlPoints.rightControlPoint;
+            const controlPoints: {startControlPoint: PointF2D, endControlPoint: PointF2D} =
+                this.calculateControlPoints(end2.x, startAngle, endAngle, transformedPoints, heightWidthRatio);
+            let startControlPoint: PointF2D = controlPoints.startControlPoint;
+            let endControlPoint: PointF2D = controlPoints.endControlPoint;
 
             // transform ControlPoints to original Coordinate System
             // (rotate back and translate back)
-            leftControlPoint = transposeMatrix.vectorMultiplication(leftControlPoint);
-            leftControlPoint.x += startX;
-            leftControlPoint.y += startY;
-            rightControlPoint = transposeMatrix.vectorMultiplication(rightControlPoint);
-            rightControlPoint.x += startX;
-            rightControlPoint.y += startY;
+            startControlPoint = transposeMatrix.vectorMultiplication(startControlPoint);
+            startControlPoint.x += startX;
+            startControlPoint.y += startY;
+            endControlPoint = transposeMatrix.vectorMultiplication(endControlPoint);
+            endControlPoint.x += startX;
+            endControlPoint.y += startY;
 
             // set private members
-            this.bezierStartPt = start;
-            this.bezierStartControlPt = leftControlPoint;
-            this.bezierEndControlPt = rightControlPoint;
-            this.bezierEndPt = end;
+            this.bezierStartPt = new PointF2D(startX, startY + startYOffset);
+            this.bezierStartControlPt = new PointF2D(startControlPoint.x, startControlPoint.y + startYOffset);
+            this.bezierEndControlPt = new PointF2D(endControlPoint.x, endControlPoint.y + endYOffset);
+            this.bezierEndPt = new PointF2D(endX, endY + endYOffset);
 
             /* for DEBUG only */
             // this.intersection = transposeMatrix.vectorMultiplication(intersectionPoint);
@@ -362,7 +425,7 @@ export class GraphicalSlur extends GraphicalCurve {
                 const diff: number = i / samplingUnit - this.bezierStartPt.x;
                 const curvePoint: PointF2D = this.calculateCurvePointAtIndex(Math.abs(diff) / distance);
 
-                // update left- and rightIndex for better accuracy
+                // update start- and endIndex for better accuracy
                 let index: number = skyBottomLineCalculator.getLeftIndexForPointX(curvePoint.x, length);
                 // update BottomLine with final slur curve:
                 if (index >= startIndex) {
@@ -399,7 +462,7 @@ export class GraphicalSlur extends GraphicalCurve {
         let endX: number = 0;
         let endY: number = 0;
 
-        if (slurStartNote !== undefined) {
+        if (slurStartNote) {
             // must be relative to StaffLine
             startX = slurStartNote.PositionAndShape.RelativePosition.x + slurStartNote.parentVoiceEntry.parentStaffEntry.PositionAndShape.RelativePosition.x
                                             + slurStartNote.parentVoiceEntry.parentStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x;
@@ -428,12 +491,12 @@ export class GraphicalSlur extends GraphicalCurve {
             if (slurStartVE.parentVoiceEntry.StemDirection === StemDirectionType.Up && this.placement === PlacementEnum.Above) {
                 startX += 0.5;
             }
-            // if (first.NoteStem !== undefined && first.NoteStem.Direction === StemEnum.StemUp && this.placement === PlacementEnum.Above) {
+            // if (first.NoteStem && first.NoteStem.Direction === StemEnum.StemUp && this.placement === PlacementEnum.Above) {
             //     startX += first.NoteStem.PositionAndShape.RelativePosition.x;
             //     startY = skyBottomLineCalculator.getSkyLineMinAtPoint(staffLine, startX);
             // } else {
             //     const last: GraphicalNote = <GraphicalNote>slurStartNote[slurEndNote.parentVoiceEntry.notes.length - 1];
-            //     if (last.NoteStem !== undefined && last.NoteStem.Direction === StemEnum.StemDown && this.placement === PlacementEnum.Below) {
+            //     if (last.NoteStem && last.NoteStem.Direction === StemEnum.StemDown && this.placement === PlacementEnum.Below) {
             //         startX += last.NoteStem.PositionAndShape.RelativePosition.x;
             //         startY = skyBottomLineCalculator.getBottomLineMaxAtPoint(staffLine, startX);
             //     } else {
@@ -443,7 +506,7 @@ export class GraphicalSlur extends GraphicalCurve {
             startX = staffLine.Measures[0].beginInstructionsWidth;
         }
 
-        if (slurEndNote !== undefined) {
+        if (slurEndNote) {
             endX = slurEndNote.PositionAndShape.RelativePosition.x + slurEndNote.parentVoiceEntry.parentStaffEntry.PositionAndShape.RelativePosition.x
                 + slurEndNote.parentVoiceEntry.parentStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x;
 
@@ -468,26 +531,26 @@ export class GraphicalSlur extends GraphicalCurve {
                 endX += 0.5;
             }
             // const first: GraphicalNote = <GraphicalNote>slurEndNote.parentVoiceEntry.notes[0];
-            // if (first.NoteStem !== undefined && first.NoteStem.Direction === StemEnum.StemUp && this.placement === PlacementEnum.Above) {
+            // if (first.NoteStem && first.NoteStem.Direction === StemEnum.StemUp && this.placement === PlacementEnum.Above) {
             //     endX += first.NoteStem.PositionAndShape.RelativePosition.x;
             //     endY = skyBottomLineCalculator.getSkyLineMinAtPoint(staffLine, endX);
             // } else {
             //     const last: GraphicalNote = <GraphicalNote>slurEndNote.parentVoiceEntry.notes[slurEndNote.parentVoiceEntry.notes.length - 1];
-            //     if (last.NoteStem !== undefined && last.NoteStem.Direction === StemEnum.StemDown && this.placement === PlacementEnum.Below) {
+            //     if (last.NoteStem && last.NoteStem.Direction === StemEnum.StemDown && this.placement === PlacementEnum.Below) {
             //         endX += last.NoteStem.PositionAndShape.RelativePosition.x;
             //         endY = skyBottomLineCalculator.getBottomLineMaxAtPoint(staffLine, endX);
             //     } else {
             //         if (this.placement === PlacementEnum.Above) {
             //             const highestNote: GraphicalNote = last;
             //             endY = highestNote.PositionAndShape.RelativePosition.y;
-            //             if (highestNote.NoteHead !== undefined) {
+            //             if (highestNote.NoteHead) {
             //                 endY += highestNote.NoteHead.PositionAndShape.BorderMarginTop;
             //             } else { endY += highestNote.PositionAndShape.BorderTop; }
             //         } else {
             //             const lowestNote: GraphicalNote = first;
             //             endY = lowestNote.parentVoiceEntry
             //             lowestNote.PositionAndShape.RelativePosition.y;
-            //             if (lowestNote.NoteHead !== undefined) {
+            //             if (lowestNote.NoteHead) {
             //                 endY += lowestNote.NoteHead.PositionAndShape.BorderMarginBottom;
             //             } else { endY += lowestNote.PositionAndShape.BorderBottom; }
             //         }
@@ -498,14 +561,14 @@ export class GraphicalSlur extends GraphicalCurve {
         }
 
         // if GraphicalSlur breaks over System, then the end/start of the curve is at the corresponding height with the known start/end
-        if (slurStartNote === undefined && slurEndNote === undefined) {
+        if (!slurStartNote && !slurEndNote) {
             startY = 0;
             endY = 0;
         }
-        if (slurStartNote === undefined) {
+        if (!slurStartNote) {
             startY = endY;
         }
-        if (slurEndNote === undefined) {
+        if (!slurEndNote) {
             endY = startY;
         }
 
@@ -521,6 +584,14 @@ export class GraphicalSlur extends GraphicalCurve {
             } else { endY += rules.SlursStartingAtSameStaffEntryYOffset; }
         }
 
+        if (this.placement === PlacementEnum.Above) {
+            startY = Math.min(startY, 1.5);
+            endY = Math.min(endY, 1.5);
+        } else {
+            startY = Math.max(startY, staffLine.StaffHeight - 1.5);
+            endY = Math.max(endY, staffLine.StaffHeight - 1.5);
+        }
+
         return {startX, startY, endX, endY};
     }
 
@@ -531,12 +602,17 @@ export class GraphicalSlur extends GraphicalCurve {
      */
     private calculatePlacement(skyBottomLineCalculator: SkyBottomLineCalculator, staffLine: StaffLine): void {
         // old version: when lyrics are given place above:
-        // if ( !this.slur.StartNote.ParentVoiceEntry.LyricsEntries.isEmpty || (this.slur.EndNote !== undefined
+        // if ( !this.slur.StartNote.ParentVoiceEntry.LyricsEntries.isEmpty || (this.slur.EndNote
         //                                     && !this.slur.EndNote.ParentVoiceEntry.LyricsEntries.isEmpty) ) {
         //     this.placement = PlacementEnum.Above;
         //     return;
         // }
 
+        if (this.rules.SlurPlacementFromXML) {
+            this.placement = this.slur.PlacementXml;
+            return;
+        }
+
         // if any StaffEntry belongs to a Measure with multiple Voices, than
         // if Slur's Start- or End-Note belongs to a LinkedVoice Below else Above
         for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
@@ -561,42 +637,35 @@ export class GraphicalSlur extends GraphicalCurve {
         const startStaffEntry: GraphicalStaffEntry = this.staffEntries[0];
         const endStaffEntry: GraphicalStaffEntry = this.staffEntries[this.staffEntries.length - 1];
 
-        // Deactivated: single Voice, opposite to StemDirection
-        // if (startStaffEntry.hasStem() && endStaffEntry.hasStem() && startStaffEntry.getStemDirection() === endStaffEntry.getStemDirection()) {
-        //     this.placement = (startStaffEntry.getStemDirection() === StemDirectionType.Up) ? PlacementEnum.Below : PlacementEnum.Above;
-        // } else {
-
-        // Placement at the side with the minimum border
-        let sX: number = startStaffEntry.PositionAndShape.BorderLeft + startStaffEntry.PositionAndShape.RelativePosition.x
-                    + startStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x;
-        let eX: number = endStaffEntry.PositionAndShape.BorderRight + endStaffEntry.PositionAndShape.RelativePosition.x
-                    + endStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x;
-
-        if (this.graceStart) {
-            sX += endStaffEntry.PositionAndShape.RelativePosition.x;
-        }
-        if (this.graceEnd) {
-            eX += endStaffEntry.staffEntryParent.PositionAndShape.RelativePosition.x;
-        }
-
-        // get SkyBottomLine borders
-        let minAbove: number = skyBottomLineCalculator.getSkyLineMinInRange(sX, eX);
-        let maxBelow: number = skyBottomLineCalculator.getBottomLineMaxInRange(sX, eX);
+        // single Voice, opposite to StemDirection
+        // here should only be one voiceEntry, so we can take graphicalVoiceEntries[0]:
+        const startStemDirection: StemDirectionType = startStaffEntry.graphicalVoiceEntries[0].parentVoiceEntry.StemDirection;
+        const endStemDirection: StemDirectionType = endStaffEntry.graphicalVoiceEntries[0].parentVoiceEntry.StemDirection;
+        if (startStemDirection  ===
+            endStemDirection) {
+            this.placement = (startStemDirection === StemDirectionType.Up) ? PlacementEnum.Below : PlacementEnum.Above;
+        } else {
+            // Placement at the side with the minimum border
+            let sX: number = startStaffEntry.PositionAndShape.BorderLeft + startStaffEntry.PositionAndShape.RelativePosition.x
+                        + startStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x;
+            let eX: number = endStaffEntry.PositionAndShape.BorderRight + endStaffEntry.PositionAndShape.RelativePosition.x
+                        + endStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x;
 
-        // get lowest and highest placed NoteHead
-        const notesMinY: number = Math.min(startStaffEntry.PositionAndShape.BorderTop,
-                                           endStaffEntry.PositionAndShape.BorderTop);
-        const notesMaxY: number = Math.max(startStaffEntry.PositionAndShape.BorderBottom,
-                                           endStaffEntry.PositionAndShape.BorderBottom);
+            if (this.graceStart) {
+                sX += endStaffEntry.PositionAndShape.RelativePosition.x;
+            }
+            if (this.graceEnd) {
+                eX += endStaffEntry.staffEntryParent.PositionAndShape.RelativePosition.x;
+            }
 
-        // get lowest and highest placed NoteHead
-        minAbove = notesMinY - minAbove;
-        maxBelow = maxBelow - notesMaxY;
+            // get SkyBottomLine borders
+            const minAbove: number = skyBottomLineCalculator.getSkyLineMinInRange(sX, eX) * -1;
+            const maxBelow: number = skyBottomLineCalculator.getBottomLineMaxInRange(sX, eX) - staffLine.StaffHeight;
 
-        if (Math.abs(maxBelow) > Math.abs(minAbove)) {
-            this.placement = PlacementEnum.Above;
-        } else { this.placement = PlacementEnum.Below; }
-        //}
+            if (maxBelow > minAbove) {
+                this.placement = PlacementEnum.Above;
+            } else { this.placement = PlacementEnum.Below; }
+        }
     }
 
     /**
@@ -619,8 +688,12 @@ export class GraphicalSlur extends GraphicalCurve {
         }
 
         for (let i: number = startIndex; i < endIndex; i++) {
-            const point: PointF2D = new PointF2D((0.5 + i) / skyBottomLineCalculator.SamplingUnit, staffLine.SkyLine[i]);
-            points.push(point);
+            const skylineValue: number = staffLine.SkyLine[i];
+            // ignore default value (= 0) which is upper border of staffline
+            if (skylineValue !== 0) {
+                const point: PointF2D = new PointF2D((0.5 + i) / skyBottomLineCalculator.SamplingUnit, skylineValue);
+                points.push(point);
+            }
         }
 
         return points;
@@ -647,8 +720,13 @@ export class GraphicalSlur extends GraphicalCurve {
         }
 
         for (let i: number = startIndex; i < endIndex; i++) {
-            const point: PointF2D = new PointF2D((0.5 + i) / skyBottomLineCalculator.SamplingUnit, staffLine.BottomLine[i]);
-            points.push(point);
+            const bottomLineValue: number = staffLine.BottomLine[i];
+
+            // ignore default value (= 4) which is lower border of staffline
+            if (bottomLineValue !== 0) {
+                const point: PointF2D = new PointF2D((0.5 + i) / skyBottomLineCalculator.SamplingUnit, bottomLineValue);
+                points.push(point);
+            }
         }
 
         return points;
@@ -674,6 +752,8 @@ export class GraphicalSlur extends GraphicalCurve {
 
         // in case all Points don't have a meaningful value or the slope between Start- and EndPoint is just bigger
         slope = Math.max(slope, Math.abs(end.y - y) / (end.x - x));
+        //limit to 80 degrees
+        slope = Math.min(slope, 5.6713);
 
         return slope;
     }
@@ -698,6 +778,8 @@ export class GraphicalSlur extends GraphicalCurve {
 
         // in case no Point has a meaningful value or the slope between Start- and EndPoint is just smaller
         slope = Math.min(slope, (y - start.y) / (x - start.x));
+        //limit to 80 degrees
+        slope = Math.max(slope, -5.6713);
 
         return slope;
     }
@@ -783,63 +865,62 @@ export class GraphicalSlur extends GraphicalCurve {
     /**
      * This method calculates the 2 ControlPoints of the SlurCurve.
      * @param endX
-     * @param leftAngle
-     * @param rightAngle
+     * @param startAngle
+     * @param endAngle
      * @param points
      */
-    private calculateControlPoints(endX: number,
-                                   leftAngle: number, rightAngle: number, points: PointF2D[]): { leftControlPoint: PointF2D, rightControlPoint: PointF2D } {
+    private calculateControlPoints(endX: number, startAngle: number, endAngle: number,
+                                   points: PointF2D[], heightWidthRatio: number): { startControlPoint: PointF2D, endControlPoint: PointF2D } {
         // calculate HeightWidthRatio between the MaxYpoint (from the points between StartPoint and EndPoint)
-            // and the X-distance from StartPoint to EndPoint
-            // use this HeightWidthRatio to get a "normalized" Factor (based on tested parameters)
-            // this Factor denotes the Length of the TangentLine of the Curve (a proportion of the X-distance from StartPoint to EndPoint)
-            // finally from this Length and the calculated Angles we get the coordinates of the Control Points
-        const heightWidthRatio: number = this.calculateHeightWidthRatio(endX, points);
-        const factor: number = GraphicalSlur.k * heightWidthRatio + GraphicalSlur.d;
-
-        const relativeLength: number = endX * factor;
-        const leftControlPoint: PointF2D = new PointF2D();
-        leftControlPoint.x = relativeLength * Math.cos(leftAngle * GraphicalSlur.degreesToRadiansFactor);
-        leftControlPoint.y = relativeLength * Math.sin(leftAngle * GraphicalSlur.degreesToRadiansFactor);
-
-        const rightControlPoint: PointF2D = new PointF2D();
-        rightControlPoint.x = endX - (relativeLength * Math.cos(rightAngle * GraphicalSlur.degreesToRadiansFactor));
-        rightControlPoint.y = -(relativeLength * Math.sin(rightAngle * GraphicalSlur.degreesToRadiansFactor));
-        return {leftControlPoint, rightControlPoint};
+        // and the X-distance from StartPoint to EndPoint
+        // use this HeightWidthRatio to get a "normalized" Factor (based on tested parameters)
+        // this Factor denotes the Length of the TangentLine of the Curve (a proportion of the X-distance from StartPoint to EndPoint)
+        // finally from this Length and the calculated Angles we get the coordinates of the Control Points
+        const factorStart: number = Math.min(0.5, Math.max(0.1, 1.7 * (startAngle / 80) * Math.pow(Math.max(heightWidthRatio, 0.05), 0.4)));
+        const factorEnd: number = Math.min(0.5, Math.max(0.1, 1.7 * (-endAngle / 80) * Math.pow(Math.max(heightWidthRatio, 0.05), 0.4)));
+
+        const startControlPoint: PointF2D = new PointF2D();
+        startControlPoint.x = endX * factorStart * Math.cos(startAngle * GraphicalSlur.degreesToRadiansFactor);
+        startControlPoint.y = endX * factorStart * Math.sin(startAngle * GraphicalSlur.degreesToRadiansFactor);
+
+        const endControlPoint: PointF2D = new PointF2D();
+        endControlPoint.x = endX - (endX * factorEnd * Math.cos(endAngle * GraphicalSlur.degreesToRadiansFactor));
+        endControlPoint.y = -(endX * factorEnd * Math.sin(endAngle * GraphicalSlur.degreesToRadiansFactor));
+        return {startControlPoint: startControlPoint, endControlPoint: endControlPoint};
     }
 
     /**
      * This method calculates the angles for the Curve's Tangent Lines.
      * @param leftAngle
      * @param rightAngle
-     * @param leftLineSlope
-     * @param rightLineSlope
+     * @param startLineSlope
+     * @param endLineSlope
      * @param maxAngle
      */
-    private calculateAngles(leftAngle: number, rightAngle: number, leftLineSlope: number, rightLineSlope: number, maxAngle: number): void {
+    private calculateAngles(minAngle: number, startLineSlope: number, endLineSlope: number, maxAngle: number):
+    {startAngle: number, endAngle: number} {
         // calculate Angles from the calculated Slopes, adding also a given angle
         const angle: number = 20;
 
-        let calculatedLeftAngle: number = Math.atan(leftLineSlope) / GraphicalSlur.degreesToRadiansFactor;
-        if (leftLineSlope > 0) {
-            calculatedLeftAngle += angle;
+        let calculatedStartAngle: number = Math.atan(startLineSlope) / GraphicalSlur.degreesToRadiansFactor;
+        if (startLineSlope > 0) {
+            calculatedStartAngle += angle;
         } else {
-            calculatedLeftAngle -= angle;
+            calculatedStartAngle -= angle;
         }
 
-        let calculatedRightAngle: number = Math.atan(rightLineSlope) / GraphicalSlur.degreesToRadiansFactor;
-        if (rightLineSlope < 0) {
-            calculatedRightAngle -= angle;
+        let calculatedEndAngle: number = Math.atan(endLineSlope) / GraphicalSlur.degreesToRadiansFactor;
+        if (endLineSlope < 0) {
+            calculatedEndAngle -= angle;
         } else {
-            calculatedRightAngle += angle;
+            calculatedEndAngle += angle;
         }
 
         // +/- 80 is the max/min allowed Angle
-        leftAngle = Math.min(Math.max(leftAngle, calculatedLeftAngle), maxAngle);
-        rightAngle = Math.max(Math.min(rightAngle, calculatedRightAngle), -maxAngle);
+        const leftAngle: number = Math.min(Math.max(minAngle, calculatedStartAngle), maxAngle);
+        const rightAngle: number = Math.max(Math.min(-minAngle, calculatedEndAngle), -maxAngle);
+        return {"startAngle": leftAngle, "endAngle": rightAngle};
     }
 
     private static degreesToRadiansFactor: number = Math.PI / 180;
-    private static k: number = 0.9;
-    private static d: number = 0.2;
 }

+ 11 - 10
src/MusicalScore/Graphical/GraphicalStaffEntry.ts

@@ -27,19 +27,19 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
         this.parentMeasure = parentMeasure;
         this.graphicalVoiceEntries = [];
         this.sourceStaffEntry = sourceStaffEntry;
-        if (staffEntryParent !== undefined) {
+        if (staffEntryParent) {
             this.staffEntryParent = staffEntryParent;
             this.parentVerticalContainer = staffEntryParent.parentVerticalContainer;
             this.PositionAndShape = new BoundingBox(this, staffEntryParent.PositionAndShape);
         } else {
             this.PositionAndShape = new BoundingBox(this, parentMeasure.PositionAndShape);
         }
-        if (sourceStaffEntry !== undefined) {
+        if (sourceStaffEntry) {
             this.relInMeasureTimestamp = sourceStaffEntry.Timestamp;
         }
     }
 
-    public graphicalChordContainer: GraphicalChordSymbolContainer;
+    public graphicalChordContainers: GraphicalChordSymbolContainer[] = [];
     public graphicalLink: GraphicalStaffEntryLink;
 
     // Extra member needed, as tie notes have no direct source entry with the right time stamp.
@@ -49,6 +49,7 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
     public graphicalVoiceEntries: GraphicalVoiceEntry[];
     public staffEntryParent: GraphicalStaffEntry;
     public parentVerticalContainer: VerticalGraphicalStaffEntryContainer;
+    public tabStaffEntry: GraphicalStaffEntry = undefined;
 
     private graphicalInstructions: AbstractGraphicalInstruction[] = [];
     private graphicalTies: GraphicalTie[] = [];
@@ -76,7 +77,7 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
      */
     public getAbsoluteTimestamp(): Fraction {
         const result: Fraction = this.parentMeasure.parentSourceMeasure.AbsoluteTimestamp.clone();
-        if (this.relInMeasureTimestamp !== undefined) {
+        if (this.relInMeasureTimestamp) {
             result.Add(this.relInMeasureTimestamp);
         }
         return result;
@@ -87,7 +88,7 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
      * @param tieNote
      * @returns {any}
      */
-    public findEndTieGraphicalNoteFromNote(tieNote: Note): GraphicalNote {
+    public findTieGraphicalNoteFromNote(tieNote: Note): GraphicalNote {
         for (const gve of this.graphicalVoiceEntries) {
             for (const graphicalNote of gve.notes) {
                 const note: Note = graphicalNote.sourceNote;
@@ -109,7 +110,7 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
      * @returns {any}
      */
     public findEndTieGraphicalNoteFromNoteWithStartingSlur(tieNote: Note, slur: Slur): GraphicalNote {
-        if (tieNote === undefined) {
+        if (!tieNote) {
             return undefined;
         }
         for (const gve of this.graphicalVoiceEntries) {
@@ -118,7 +119,7 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
             }
             for (const graphicalNote of gve.notes) {
                 const note: Note = graphicalNote.sourceNote;
-                if (note.NoteTie !== undefined && note.NoteSlurs.indexOf(slur) !== -1) {
+                if (note.NoteTie && note.NoteSlurs.indexOf(slur) !== -1) {
                     return graphicalNote;
                 }
             }
@@ -127,7 +128,7 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
     }
 
     public findGraphicalNoteFromGraceNote(graceNote: Note): GraphicalNote {
-        if (graceNote === undefined) {
+        if (!graceNote) {
             return undefined;
         }
         for (const gve of this.graphicalVoiceEntries) {
@@ -144,7 +145,7 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
     }
 
     public findGraphicalNoteFromNote(note: Note): GraphicalNote {
-        if (note === undefined) {
+        if (!note) {
             return undefined;
         }
         for (const gve of this.graphicalVoiceEntries) {
@@ -190,7 +191,7 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
      * @returns {boolean}
      */
     public isVoiceEntryPartOfLinkedVoiceEntry(voiceEntry: VoiceEntry): boolean {
-        if (this.sourceStaffEntry.Link !== undefined) {
+        if (this.sourceStaffEntry.Link) {
             for (let idx: number = 0, len: number = this.sourceStaffEntry.Link.LinkStaffEntries.length; idx < len; ++idx) {
                 const sEntry: SourceStaffEntry = this.sourceStaffEntry.Link.LinkStaffEntries[idx];
                 if (sEntry.VoiceEntries.indexOf(voiceEntry) !== -1 && sEntry !== this.sourceStaffEntry) {

+ 2 - 2
src/MusicalScore/Graphical/GraphicalStaffEntryLink.ts

@@ -24,7 +24,7 @@ export class GraphicalStaffEntryLink {
     }
     public isFilled(): boolean {
         for (let i: number = 0; i < this.graphicalLinkedStaffEntries.length; i++) {
-            if (this.graphicalLinkedStaffEntries[i] === undefined) {
+            if (!this.graphicalLinkedStaffEntries[i]) {
                 return false;
             }
         }
@@ -43,7 +43,7 @@ export class GraphicalStaffEntryLink {
                 const graphicalLinkedStaffEntry: GraphicalStaffEntry = this.graphicalLinkedStaffEntries[idx];
                 for (const gve of graphicalLinkedStaffEntry.graphicalVoiceEntries) {
                     for (const graphicalNote of gve.notes) {
-                        if (graphicalNote.sourceNote.ParentStaffEntry.Link !== undefined
+                        if (graphicalNote.sourceNote.ParentStaffEntry.Link
                             && graphicalNote.sourceNote.ParentVoiceEntry === this.staffEntryLink.GetVoiceEntry) {
                             notes.push(graphicalNote);
                         }

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

@@ -21,6 +21,9 @@ export class GraphicalTie {
     public get StartNote(): GraphicalNote {
         return this.startNote;
     }
+    public get Tie(): Tie {
+        return this.tie;
+    }
     public set StartNote(value: GraphicalNote) {
         this.startNote = value;
     }

+ 39 - 0
src/MusicalScore/Graphical/GraphicalUnknownExpression.ts

@@ -0,0 +1,39 @@
+
+import { StaffLine } from "./StaffLine";
+import { GraphicalLabel } from "./GraphicalLabel";
+import { AbstractGraphicalExpression } from "./AbstractGraphicalExpression";
+import { PlacementEnum, AbstractExpression } from "../VoiceData/Expressions/AbstractExpression";
+import { MultiExpression } from "../VoiceData/Expressions/MultiExpression";
+import { SkyBottomLineCalculator } from "./SkyBottomLineCalculator";
+import log from "loglevel";
+import { SourceMeasure } from "../VoiceData/SourceMeasure";
+
+export class GraphicalUnknownExpression extends AbstractGraphicalExpression {
+    public sourceMultiExpression: MultiExpression;
+
+    constructor(staffLine: StaffLine, label: GraphicalLabel, placement: PlacementEnum, measure: SourceMeasure,
+                sourceMultiExpression: MultiExpression = undefined) {
+        super(staffLine, new AbstractExpression(placement), measure);
+        this.label = label;
+        this.sourceMultiExpression = sourceMultiExpression;
+    }
+
+    public updateSkyBottomLine(): void {
+        // update Sky-BottomLine
+        const skyBottomLineCalculator: SkyBottomLineCalculator = this.parentStaffLine.SkyBottomLineCalculator;
+        const left: number = this.label.PositionAndShape.RelativePosition.x + this.label.PositionAndShape.BorderMarginLeft;
+        const right: number = this.label.PositionAndShape.RelativePosition.x + this.label.PositionAndShape.BorderMarginRight;
+        switch (this.Placement) {
+            case PlacementEnum.Above:
+                const yValueAbove: number = this.label.PositionAndShape.BorderMarginTop + this.label.PositionAndShape.RelativePosition.y;
+                skyBottomLineCalculator.updateSkyLineInRange(left, right, yValueAbove);
+                break;
+            case PlacementEnum.Below:
+                const yValueBelow: number = this.label.PositionAndShape.BorderMarginBottom + this.label.PositionAndShape.RelativePosition.y;
+                skyBottomLineCalculator.updateBottomLineInRange(left, right, yValueBelow);
+                break;
+            default:
+                log.error("Placement for GraphicalUnknownExpression is unknown");
+        }
+    }
+}

+ 5 - 60
src/MusicalScore/Graphical/GraphicalVoiceEntry.ts

@@ -4,7 +4,6 @@ import { BoundingBox } from "./BoundingBox";
 import { GraphicalNote } from "./GraphicalNote";
 import { GraphicalStaffEntry } from "./GraphicalStaffEntry";
 import { OctaveEnum } from "../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
-import { VexFlowVoiceEntry } from "./VexFlow/VexFlowVoiceEntry";
 import { EngravingRules } from "./EngravingRules";
 
 /**
@@ -17,6 +16,8 @@ export class GraphicalVoiceEntry extends GraphicalObject {
         this.parentStaffEntry = parentStaffEntry;
         this.PositionAndShape = new BoundingBox(this, parentStaffEntry ? parentStaffEntry.PositionAndShape : undefined, true);
         this.notes = [];
+        this.rules = parentStaffEntry ?
+                        parentStaffEntry.parentMeasure.parentSourceMeasure.Rules : new EngravingRules();
     }
 
     public parentVoiceEntry: VoiceEntry;
@@ -24,6 +25,7 @@ export class GraphicalVoiceEntry extends GraphicalObject {
     public notes: GraphicalNote[];
     /** Contains octave shifts affecting this voice entry, caused by octave brackets. */
     public octaveShiftValue: OctaveEnum;
+    protected rules: EngravingRules;
 
     /** Sort this entry's notes by pitch.
      * Notes need to be sorted for Vexflow StaveNote creation.
@@ -35,66 +37,9 @@ export class GraphicalVoiceEntry extends GraphicalObject {
         });
     }
 
-    /** (Re-)color notes and stems by setting their Vexflow styles.
-     * Could be made redundant by a Vexflow PR, but Vexflow needs more solid and permanent color methods/variables for that
-     * See VexFlowConverter.StaveNote()
+    /** (Re-)color notes and stems
      */
     public color(): void {
-        const defaultColorNotehead: string = EngravingRules.Rules.DefaultColorNotehead;
-        const defaultColorRest: string = EngravingRules.Rules.DefaultColorRest;
-        const defaultColorStem: string = EngravingRules.Rules.DefaultColorStem;
-
-        const vfStaveNote: any = (<VexFlowVoiceEntry>(this as any)).vfStaveNote;
-        for (let i: number = 0; i < this.notes.length; i++) {
-            const note: GraphicalNote = this.notes[i];
-
-            let noteheadColor: string = note.sourceNote.NoteheadColor;
-
-            // DEBUG runtime coloring test
-            /*const testColor: string = "#FF0000";
-            if (i === 2 && Math.random() < 0.1 && note.sourceNote.NoteheadColor !== testColor) {
-                const measureNumber: number = note.parentVoiceEntry.parentStaffEntry.parentMeasure.MeasureNumber;
-                noteheadColor = testColor;
-                console.log("color changed to " + noteheadColor + " of this note:\n" + note.sourceNote.Pitch.ToString() +
-                    ", in measure #" + measureNumber);
-            }*/
-
-            if (!noteheadColor) {
-                if (!note.sourceNote.isRest() && defaultColorNotehead) {
-                    noteheadColor = defaultColorNotehead;
-                } else if (note.sourceNote.isRest() && defaultColorRest) {
-                    noteheadColor = defaultColorRest;
-                }
-            }
-            if (noteheadColor) {
-                note.sourceNote.NoteheadColor = noteheadColor;
-            } else {
-                continue;
-            }
-
-            if (vfStaveNote) {
-                if (vfStaveNote.note_heads) { // see VexFlowConverter, needs Vexflow PR
-                    const notehead: any = vfStaveNote.note_heads[i];
-                    if (notehead) {
-                        vfStaveNote.note_heads[i].setStyle({ fillStyle: noteheadColor, strokeStyle: noteheadColor });
-                    }
-                }
-            }
-        }
-
-        // color stems
-        let stemColor: string = this.parentVoiceEntry.StemColor;
-        if (!stemColor && defaultColorStem) {
-            stemColor = defaultColorStem;
-        }
-        const stemStyle: Object = { fillStyle: stemColor, strokeStyle: stemColor };
-
-        if (stemColor && vfStaveNote.setStemStyle) {
-            this.parentVoiceEntry.StemColor = stemColor;
-            vfStaveNote.setStemStyle(stemStyle);
-            if (vfStaveNote.flag && vfStaveNote.setFlagStyle && EngravingRules.Rules.ColorFlags) {
-                vfStaveNote.setFlagStyle(stemStyle);
-            }
-        }
+        // override
     }
 }

Datei-Diff unterdrückt, da er zu groß ist
+ 373 - 230
src/MusicalScore/Graphical/MusicSheetCalculator.ts


+ 45 - 31
src/MusicalScore/Graphical/MusicSheetDrawer.ts

@@ -25,6 +25,7 @@ import {MusicSymbolDrawingStyle, PhonicScoreModes} from "./DrawingMode";
 import {GraphicalObject} from "./GraphicalObject";
 import { GraphicalInstantaneousDynamicExpression } from "./GraphicalInstantaneousDynamicExpression";
 import { GraphicalContinuousDynamicExpression } from "./GraphicalContinuousDynamicExpression";
+// import { FontStyles } from "../../Common/Enums/FontStyles";
 
 /**
  * Draw a [[GraphicalMusicSheet]] (through the .drawSheet method)
@@ -57,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) {
@@ -69,6 +71,11 @@ export abstract class MusicSheetDrawer {
         this.drawSplitScreenLine();
         if (this.drawingParameters.drawCursors) {
             for (const line of graphicalMusicSheet.Cursors) {
+                if (!line) {
+                    // TODO GraphicalMusicSheet.calculateCursorLineAtTimestamp() can return undefined.
+                    // why does this happen in the VexFlowMusicSheetDrawer_Test? (it("draws cursor..."))
+                    continue;
+                }
                 const psi: BoundingBox = new BoundingBox(line);
                 psi.AbsolutePosition = line.Start;
                 psi.BorderBottom = line.End.y - line.Start.y;
@@ -83,8 +90,10 @@ export abstract class MusicSheetDrawer {
         if (this.drawingParameters.drawScrollIndicator) {
             this.drawScrollIndicator();
         }
-        // Draw all the pages
-        for (const page of this.graphicalMusicSheet.MusicPages) {
+        // Draw the pages
+        const pagesToDraw: number = Math.min(this.graphicalMusicSheet.MusicPages.length, this.rules.MaxPageToDrawNumber);
+        for (let i: number = 0; i < pagesToDraw; i ++) {
+            const page: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[i];
             this.drawPage(page);
         }
     }
@@ -141,10 +150,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()
@@ -182,7 +192,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 {
@@ -295,20 +306,22 @@ export abstract class MusicSheetDrawer {
         for (const staffLine of musicSystem.StaffLines) {
             this.drawStaffLine(staffLine);
 
-            // draw lyric dashes
-            if (staffLine.LyricsDashes.length > 0) {
-                this.drawDashes(staffLine.LyricsDashes);
-            }
+            if (this.rules.RenderLyrics) {
+                // draw lyric dashes
+                if (staffLine.LyricsDashes.length > 0) {
+                    this.drawDashes(staffLine.LyricsDashes);
+                }
 
-            // draw lyric lines (e.g. LyricExtends: "dich,___")
-            if (staffLine.LyricLines.length > 0) {
-                this.drawLyricLines(staffLine.LyricLines, staffLine);
+                // draw lyric lines (e.g. LyricExtends: "dich,___")
+                if (staffLine.LyricLines.length > 0) {
+                    this.drawLyricLines(staffLine.LyricLines, staffLine);
+                }
             }
         }
         for (const systemLine of musicSystem.SystemLines) {
             this.drawSystemLineObject(systemLine);
         }
-        if (musicSystem === musicSystem.Parent.MusicSystems[0] && musicSystem.Parent === musicSystem.Parent.Parent.MusicPages[0]) {
+        if (musicSystem.Parent === musicSystem.Parent.Parent.MusicPages[0]) {
             for (const label of musicSystem.Labels) {
                 this.drawLabel(label, <number>GraphicalLayers.Notes);
             }
@@ -349,10 +362,11 @@ export abstract class MusicSheetDrawer {
             this.drawMeasure(measure);
         }
 
-        if (staffLine.LyricsDashes.length > 0) {
-            this.drawDashes(staffLine.LyricsDashes);
+        if (this.rules.RenderLyrics) {
+            if (staffLine.LyricsDashes.length > 0) {
+                this.drawDashes(staffLine.LyricsDashes);
+            }
         }
-
         this.drawOctaveShifts(staffLine);
 
         this.drawExpressions(staffLine);
@@ -375,7 +389,7 @@ export abstract class MusicSheetDrawer {
             lyricLine.End.y += staffLine.PositionAndShape.AbsolutePosition.y;
             lyricLine.Start.x += staffLine.PositionAndShape.AbsolutePosition.x;
             lyricLine.End.x += staffLine.PositionAndShape.AbsolutePosition.x;
-            this.drawGraphicalLine(lyricLine, EngravingRules.Rules.LyricUnderscoreLineWidth);
+            this.drawGraphicalLine(lyricLine, this.rules.LyricUnderscoreLineWidth);
         });
     }
 
@@ -392,7 +406,7 @@ export abstract class MusicSheetDrawer {
         this.drawLine(graphicalLine.Start, graphicalLine.End, colorOrStyle, lineWidth);
     }
 
-    public drawLine(start: PointF2D, stop: PointF2D, color: string = "#FF0000FF", lineWidth: number): void {
+    protected drawLine(start: PointF2D, stop: PointF2D, color: string = "#FF0000FF", lineWidth: number): void {
         // implemented by subclass (VexFlowMusicSheetDrawer)
     }
 
@@ -414,7 +428,7 @@ export abstract class MusicSheetDrawer {
     }
 
     protected drawStaffLines(staffLine: StaffLine): void {
-        if (staffLine.StaffLines !== undefined) {
+        if (staffLine.StaffLines) {
             const position: PointF2D = staffLine.PositionAndShape.AbsolutePosition;
             for (let i: number = 0; i < 5; i++) {
                 this.drawLineAsHorizontalRectangleWithOffset(staffLine.StaffLines[i], position, <number>GraphicalLayers.Notes);
@@ -460,7 +474,7 @@ export abstract class MusicSheetDrawer {
         this.graphicalMusicSheet.LeadSheet = value;
     }
 
-    private drawPage(page: GraphicalMusicPage): void {
+    protected drawPage(page: GraphicalMusicPage): void {
         if (!this.isVisible(page.PositionAndShape)) {
             return;
         }
@@ -480,7 +494,6 @@ export abstract class MusicSheetDrawer {
         if (this.drawableBoundingBoxElement) {
             this.drawBoundingBoxes(page.PositionAndShape, 0, this.drawableBoundingBoxElement);
         }
-
     }
 
     /**
@@ -512,7 +525,8 @@ export abstract class MusicSheetDrawer {
 
             tmpRect = this.applyScreenTransformationForRect(tmpRect);
             this.renderRectangle(tmpRect, <number>GraphicalLayers.Background, layer, 0.5);
-            this.renderLabel(new GraphicalLabel(new Label(dataObjectString), 0.8, TextAlignmentEnum.CenterCenter),
+            const label: Label = new Label(dataObjectString);
+            this.renderLabel(new GraphicalLabel(label, 0.8, TextAlignmentEnum.CenterCenter, this.rules),
                              layer, tmpRect.width, tmpRect.height, tmpRect.height, new PointF2D(tmpRect.x, tmpRect.y + 12));
         }
         layer++;
@@ -521,17 +535,17 @@ export abstract class MusicSheetDrawer {
 
     private drawMarkedAreas(system: MusicSystem): void {
         for (const markedArea of system.GraphicalMarkedAreas) {
-            if (markedArea !== undefined) {
-                if (markedArea.systemRectangle !== undefined) {
+            if (markedArea) {
+                if (markedArea.systemRectangle) {
                     this.drawRectangle(markedArea.systemRectangle, <number>GraphicalLayers.Background);
                 }
-                if (markedArea.settings !== undefined) {
+                if (markedArea.settings) {
                     this.drawLabel(markedArea.settings, <number>GraphicalLayers.Comment);
                 }
-                if (markedArea.labelRectangle !== undefined) {
+                if (markedArea.labelRectangle) {
                     this.drawRectangle(markedArea.labelRectangle, <number>GraphicalLayers.Background);
                 }
-                if (markedArea.label !== undefined) {
+                if (markedArea.label) {
                     this.drawLabel(markedArea.label, <number>GraphicalLayers.Comment);
                 }
             }
@@ -540,11 +554,11 @@ export abstract class MusicSheetDrawer {
 
     private drawComment(system: MusicSystem): void {
         for (const comment of system.GraphicalComments) {
-            if (comment !== undefined) {
-                if (comment.settings !== undefined) {
+            if (comment) {
+                if (comment.settings) {
                     this.drawLabel(comment.settings, <number>GraphicalLayers.Comment);
                 }
-                if (comment.label !== undefined) {
+                if (comment.label) {
                     this.drawLabel(comment.label, <number>GraphicalLayers.Comment);
                 }
             }

+ 89 - 51
src/MusicalScore/Graphical/MusicSystem.ts

@@ -13,23 +13,29 @@ import {EngravingRules} from "./EngravingRules";
 import {PointF2D} from "../../Common/DataObjects/PointF2D";
 import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
 import {SystemLinesEnum} from "./SystemLinesEnum";
-import Dictionary from "typescript-collections/dist/lib/Dictionary";
+import { Dictionary } from "typescript-collections";
 import {GraphicalComment} from "./GraphicalComment";
 import {GraphicalMarkedArea} from "./GraphicalMarkedArea";
 import {SystemLine} from "./SystemLine";
 import {SystemLinePosition} from "./SystemLinePosition";
 import {Staff} from "../VoiceData/Staff";
+import { Label } from "../Label";
 
 /**
  * A MusicSystem contains the [[StaffLine]]s for all instruments, until a line break
  */
 export abstract class MusicSystem extends GraphicalObject {
     public needsToBeRedrawn: boolean = true;
+    public rules: EngravingRules;
     protected parent: GraphicalMusicPage;
     protected id: number;
     protected staffLines: StaffLine[] = [];
     protected graphicalMeasures: GraphicalMeasure[][] = [];
-    protected labels: Dictionary<GraphicalLabel, Instrument> = new Dictionary<GraphicalLabel, Instrument>();
+    /** Dictionary of (Instruments and) labels.
+     * note that the key needs to be unique, GraphicalLabel is not unique yet.
+     * That is why the labels are labels.values() and not labels.keys().
+     */
+    protected labels: Dictionary<Instrument, GraphicalLabel> = new Dictionary<Instrument, GraphicalLabel>();
     protected measureNumberLabels: GraphicalLabel[] = [];
     protected maxLabelLength: number;
     protected objectsToRedraw: [Object[], Object][] = [];
@@ -38,15 +44,13 @@ export abstract class MusicSystem extends GraphicalObject {
     protected graphicalMarkedAreas: GraphicalMarkedArea[] = [];
     protected graphicalComments: GraphicalComment[] = [];
     protected systemLines: SystemLine[] = [];
-    protected rules: EngravingRules;
+    public breaksPage: boolean = false;
 
-    constructor(parent: GraphicalMusicPage, id: number) {
+    constructor(id: number) {
         super();
-        this.parent = parent;
         this.id = id;
-        this.boundingBox = new BoundingBox(this, parent.PositionAndShape);
+        this.boundingBox = new BoundingBox(this);
         this.maxLabelLength = 0.0;
-        this.rules = this.parent.Parent.ParentMusicSheet.Rules;
     }
 
     public get Parent(): GraphicalMusicPage {
@@ -54,7 +58,16 @@ export abstract class MusicSystem extends GraphicalObject {
     }
 
     public set Parent(value: GraphicalMusicPage) {
+        // remove from old page
+        if (this.parent) {
+            const index: number = this.parent.MusicSystems.indexOf(this, 0);
+            if (index > -1) {
+                this.parent.MusicSystems.splice(index, 1);
+            }
+        }
+
         this.parent = value;
+        this.boundingBox.Parent = value.PositionAndShape;
     }
 
     public get NextSystem(): MusicSystem {
@@ -75,7 +88,7 @@ export abstract class MusicSystem extends GraphicalObject {
     }
 
     public get Labels(): GraphicalLabel[] {
-        return this.labels.keys();
+        return this.labels.values();
     }
 
     public get ObjectsToRedraw(): [Object[], Object][] {
@@ -111,9 +124,9 @@ export abstract class MusicSystem extends GraphicalObject {
      * @param lineWidth
      * @param systemLabelsRightMargin
      */
-    public createSystemLeftLine(lineWidth: number, systemLabelsRightMargin: number): void {
+    public createSystemLeftLine(lineWidth: number, systemLabelsRightMargin: number, isFirstSystem: boolean): void {
         let xPosition: number = -lineWidth / 2;
-        if (this === this.parent.MusicSystems[0] && this.parent === this.parent.Parent.MusicPages[0]) {
+        if (isFirstSystem) {
             xPosition = this.maxLabelLength + systemLabelsRightMargin - lineWidth / 2;
         }
         const top: GraphicalMeasure = this.staffLines[0].Measures[0];
@@ -143,6 +156,7 @@ export abstract class MusicSystem extends GraphicalObject {
      */
     public createVerticalLineForMeasure(xPosition: number, lineWidth: number, lineType: SystemLinesEnum, linePosition: SystemLinePosition,
                                         measureIndex: number, measure: GraphicalMeasure): void {
+        //return; // TODO check why there's a bold line here for the double barline sample
         const staffLine: StaffLine = measure.ParentStaffLine;
         const staffLineRelative: PointF2D = new PointF2D(staffLine.PositionAndShape.RelativePosition.x,
                                                          staffLine.PositionAndShape.RelativePosition.y);
@@ -189,7 +203,7 @@ export abstract class MusicSystem extends GraphicalObject {
     public AddGraphicalMeasures(graphicalMeasures: GraphicalMeasure[]): void {
         for (let idx: number = 0, len: number = graphicalMeasures.length; idx < len; ++idx) {
             const graphicalMeasure: GraphicalMeasure = graphicalMeasures[idx];
-            graphicalMeasure.parentMusicSystem = this;
+            graphicalMeasure.ParentMusicSystem = this;
         }
         this.graphicalMeasures.push(graphicalMeasures);
     }
@@ -222,7 +236,7 @@ export abstract class MusicSystem extends GraphicalObject {
                         lastStaffLine = staffLine;
                     }
                 }
-                if (firstStaffLine !== undefined && lastStaffLine !== undefined) {
+                if (firstStaffLine && lastStaffLine) {
                     this.createInstrumentBracket(firstStaffLine, lastStaffLine);
                 }
             }
@@ -243,7 +257,7 @@ export abstract class MusicSystem extends GraphicalObject {
             }
             const instrument1: Instrument = this.findFirstVisibleInstrumentInInstrumentalGroup(instrumentGroup);
             const instrument2: Instrument = this.findLastVisibleInstrumentInInstrumentalGroup(instrumentGroup);
-            if (instrument1 === undefined || instrument2 === undefined) {
+            if (!instrument1 || !instrument2) {
                 continue;
             }
             let firstStaffLine: StaffLine = undefined;
@@ -257,7 +271,7 @@ export abstract class MusicSystem extends GraphicalObject {
                     lastStaffLine = staffLine;
                 }
             }
-            if (firstStaffLine !== undefined && lastStaffLine !== undefined) {
+            if (firstStaffLine && lastStaffLine) {
                 this.createGroupBracket(firstStaffLine, lastStaffLine, recursionDepth);
             }
             if (instrumentGroup.InstrumentalGroups.length < 1) {
@@ -273,60 +287,84 @@ export abstract class MusicSystem extends GraphicalObject {
      * @param systemLabelsRightMargin
      * @param labelMarginBorderFactor
      */
-    public createMusicSystemLabel(instrumentLabelTextHeight: number, systemLabelsRightMargin: number, labelMarginBorderFactor: number): void {
-        if (this.parent === this.parent.Parent.MusicPages[0] && this === this.parent.MusicSystems[0]) {
-            const instruments: Instrument[] = this.parent.Parent.ParentMusicSheet.getVisibleInstruments();
-            for (let idx: number = 0, len: number = instruments.length; idx < len; ++idx) {
-                const instrument: Instrument = instruments[idx];
+    public createMusicSystemLabel(  instrumentLabelTextHeight: number, systemLabelsRightMargin: number,
+                                    labelMarginBorderFactor: number, isFirstSystem: boolean = false): void {
+
+        const originalSystemLabelsRightMargin: number = systemLabelsRightMargin;
+        for (let idx: number = 0, len: number = this.staffLines.length; idx < len; ++idx) {
+            const instrument: Instrument = this.staffLines[idx].ParentStaff.ParentInstrument;
+            let instrNameLabel: Label;
+            if (isFirstSystem) {
+                instrNameLabel = instrument.NameLabel;
+                if (!this.rules.RenderPartNames || !instrNameLabel?.print) {
+                    instrNameLabel = new Label("", instrument.NameLabel.textAlignment, instrument.NameLabel.font);
+                    systemLabelsRightMargin = 0; // might affect lyricist/tempo placement. but without this there's still some extra x-spacing.
+                }
+            } else {
+                if (!this.rules.RenderPartAbbreviations || !this.rules.RenderPartNames // don't render abbreviations if we don't render part names
+                    || this.staffLines.length === 1 // don't render part abbreviations if there's only one instrument/part (could be an option in the future)
+                    || !instrument.PartAbbreviation
+                    || instrument.PartAbbreviation === "") {
+                    return;
+                }
+                const labelText: string = instrument.PartAbbreviation;
+                // const labelText: string = instrument.NameLabel.text[0] + ".";
+                instrNameLabel = new Label(labelText, instrument.NameLabel.textAlignment, instrument.NameLabel.font);
+            }
+            if (instrument?.NameLabel?.print) {
                 const graphicalLabel: GraphicalLabel = new GraphicalLabel(
-                    instrument.NameLabel, instrumentLabelTextHeight, TextAlignmentEnum.LeftCenter, this.boundingBox
+                    instrNameLabel, instrumentLabelTextHeight, TextAlignmentEnum.LeftCenter, this.rules, this.boundingBox
                 );
                 graphicalLabel.setLabelPositionAndShapeBorders();
-                this.labels.setValue(graphicalLabel, instrument);
+                this.labels.setValue(instrument, graphicalLabel);
                 // X-Position will be 0 (Label starts at the same PointF_2D with MusicSystem)
                 // Y-Position will be calculated after the y-Spacing
                 // graphicalLabel.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
+            } else {
+                systemLabelsRightMargin = 0;
             }
+        }
 
-            // calculate maxLabelLength (needed for X-Spacing)
-            this.maxLabelLength = 0.0;
-            const labels: GraphicalLabel[] = this.labels.keys();
-            for (let idx: number = 0, len: number = labels.length; idx < len; ++idx) {
-                const label: GraphicalLabel = labels[idx];
-                if (label.PositionAndShape.Size.width > this.maxLabelLength) {
-                    this.maxLabelLength = label.PositionAndShape.Size.width;
-                }
+        // calculate maxLabelLength (needed for X-Spacing)
+        this.maxLabelLength = 0.0;
+        const labels: GraphicalLabel[] = this.labels.values();
+        for (let idx: number = 0, len: number = labels.length; idx < len; ++idx) {
+            const label: GraphicalLabel = labels[idx];
+            if (!label.Label.print) {
+                continue;
+            }
+            if (label.PositionAndShape.Size.width > this.maxLabelLength) {
+                this.maxLabelLength = label.PositionAndShape.Size.width;
+                systemLabelsRightMargin = originalSystemLabelsRightMargin;
             }
-            this.updateMusicSystemStaffLineXPosition(systemLabelsRightMargin);
         }
+        this.updateMusicSystemStaffLineXPosition(systemLabelsRightMargin);
     }
 
     /**
      * Set the Y-Positions for the MusicSystem's Labels.
      */
     public setMusicSystemLabelsYPosition(): void {
-        if (this.parent === this.parent.Parent.MusicPages[0] && this === this.parent.MusicSystems[0]) {
-            this.labels.forEach((key: GraphicalLabel, value: Instrument): void => {
-                let ypositionSum: number = 0;
-                let staffCounter: number = 0;
-                for (let i: number = 0; i < this.staffLines.length; i++) {
-                    if (this.staffLines[i].ParentStaff.ParentInstrument === value) {
-                        for (let j: number = i; j < this.staffLines.length; j++) {
-                            const staffLine: StaffLine = this.staffLines[j];
-                            if (staffLine.ParentStaff.ParentInstrument !== value) {
-                                break;
-                            }
-                            ypositionSum += staffLine.PositionAndShape.RelativePosition.y;
-                            staffCounter++;
+        this.labels.forEach((key: Instrument, value: GraphicalLabel): void => {
+            let ypositionSum: number = 0;
+            let staffCounter: number = 0;
+            for (let i: number = 0; i < this.staffLines.length; i++) {
+                if (this.staffLines[i].ParentStaff.ParentInstrument === key) {
+                    for (let j: number = i; j < this.staffLines.length; j++) {
+                        const staffLine: StaffLine = this.staffLines[j];
+                        if (staffLine.ParentStaff.ParentInstrument !== key) {
+                            break;
                         }
-                        break;
+                        ypositionSum += staffLine.PositionAndShape.RelativePosition.y;
+                        staffCounter++;
                     }
+                    break;
                 }
-                if (staffCounter > 0) {
-                    key.PositionAndShape.RelativePosition = new PointF2D(0.0, ypositionSum / staffCounter + 2.0);
-                }
-            });
-        }
+            }
+            if (staffCounter > 0) {
+                value.PositionAndShape.RelativePosition = new PointF2D(0.0, ypositionSum / staffCounter + 2.0);
+            }
+        });
     }
 
     /**
@@ -342,7 +380,7 @@ export abstract class MusicSystem extends GraphicalObject {
                 const measure: GraphicalMeasure = this.staffLines[i].Measures[idx];
                 for (let idx2: number = 0, len2: number = measure.staffEntries.length; idx2 < len2; ++idx2) {
                     const staffEntry: GraphicalStaffEntry = measure.staffEntries[idx2];
-                    if (staffEntry.sourceStaffEntry.Link !== undefined) {
+                    if (staffEntry.sourceStaffEntry.Link) {
                         first = true;
                     }
                 }
@@ -351,7 +389,7 @@ export abstract class MusicSystem extends GraphicalObject {
                 const measure: GraphicalMeasure = this.staffLines[i + 1].Measures[idx];
                 for (let idx2: number = 0, len2: number = measure.staffEntries.length; idx2 < len2; ++idx2) {
                     const staffEntry: GraphicalStaffEntry = measure.staffEntries[idx2];
-                    if (staffEntry.sourceStaffEntry.Link !== undefined) {
+                    if (staffEntry.sourceStaffEntry.Link) {
                         second = true;
                     }
                 }

+ 493 - 162
src/MusicalScore/Graphical/MusicSystemBuilder.ts

@@ -22,33 +22,30 @@ import {CollectionUtil} from "../../Util/CollectionUtil";
 import {SystemLinePosition} from "./SystemLinePosition";
 
 export class MusicSystemBuilder {
-    private measureList: GraphicalMeasure[][];
-    private graphicalMusicSheet: GraphicalMusicSheet;
-    private currentMusicPage: GraphicalMusicPage;
-    private currentPageHeight: number;
-    private currentSystemParams: SystemBuildParameters;
-    private numberOfVisibleStaffLines: number;
-    private rules: EngravingRules;
-    private measureListIndex: number;
+    protected measureList: GraphicalMeasure[][];
+    protected graphicalMusicSheet: GraphicalMusicSheet;
+    protected currentSystemParams: SystemBuildParameters;
+    protected numberOfVisibleStaffLines: number;
+    protected rules: EngravingRules;
+    protected measureListIndex: number;
+    protected musicSystems: MusicSystem[] = [];
 
     /**
      * Does the mapping from the currently visible staves to the global staff-list of the music sheet.
      */
-    private visibleStaffIndices: number[];
-    private activeRhythm: RhythmInstruction[];
-    private activeKeys: KeyInstruction[];
-    private activeClefs: ClefInstruction[];
-    private globalSystemIndex: number = 0;
-    private leadSheet: boolean = false;
+    protected visibleStaffIndices: number[];
+    protected activeRhythm: RhythmInstruction[];
+    protected activeKeys: KeyInstruction[];
+    protected activeClefs: ClefInstruction[];
+    protected globalSystemIndex: number = 0;
+    protected leadSheet: boolean = false;
 
     public initialize(
         graphicalMusicSheet: GraphicalMusicSheet, measureList: GraphicalMeasure[][], numberOfStaffLines: number): void {
         this.leadSheet = graphicalMusicSheet.LeadSheet;
         this.graphicalMusicSheet = graphicalMusicSheet;
-        this.rules = this.graphicalMusicSheet.ParentMusicSheet.rules;
+        this.rules = this.graphicalMusicSheet.ParentMusicSheet.Rules;
         this.measureList = measureList;
-        this.currentMusicPage = this.createMusicPage();
-        this.currentPageHeight = 0.0;
         this.numberOfVisibleStaffLines = numberOfStaffLines;
         this.activeRhythm = new Array(this.numberOfVisibleStaffLines);
         this.activeKeys = new Array(this.numberOfVisibleStaffLines);
@@ -56,39 +53,36 @@ export class MusicSystemBuilder {
         this.initializeActiveInstructions(this.measureList[0]);
     }
 
-    public buildMusicSystems(): void {
-        let previousMeasureEndsSystem: boolean = false;
+    public buildMusicSystems(): MusicSystem[] {
         const systemMaxWidth: number = this.getFullPageSystemWidth();
+        let prevMeasureEndsPart: boolean = false;
         this.measureListIndex = 0;
         this.currentSystemParams = new SystemBuildParameters();
 
         // the first System - create also its Labels
         this.currentSystemParams.currentSystem = this.initMusicSystem();
-        this.layoutSystemStaves();
-        if (EngravingRules.Rules.RenderInstrumentNames) {
-            this.currentSystemParams.currentSystem.createMusicSystemLabel(
-                this.rules.InstrumentLabelTextHeight,
-                this.rules.SystemLabelsRightMargin,
-                this.rules.LabelMarginBorderFactor
-            );
-        }
-        this.currentPageHeight += this.currentSystemParams.currentSystem.PositionAndShape.RelativePosition.y;
 
-        let numberOfMeasures: number = 0;
-        for (let idx: number = 0, len: number = this.measureList.length; idx < len; ++idx) {
-            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.
-        while (this.measureListIndex < 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 < 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();
             }
             const sourceMeasure: SourceMeasure = graphicalMeasures[0].parentSourceMeasure;
-            const sourceMeasureEndsSystem: boolean = sourceMeasure.BreakSystemAfter;
+            const sourceMeasureEndsPart: boolean = sourceMeasure.HasEndLine;
+            const sourceMeasureBreaksSystem: boolean = sourceMeasureEndsPart && this.rules.NewPartAndSystemAfterFinalBarline;
             const isSystemStartMeasure: boolean = this.currentSystemParams.IsSystemStartMeasure();
             const isFirstSourceMeasure: boolean = sourceMeasure === this.graphicalMusicSheet.ParentMusicSheet.getFirstSourceMeasure();
             let currentMeasureBeginInstructionsWidth: number = this.rules.MeasureLeftMargin;
@@ -102,7 +96,14 @@ export class MusicSystemBuilder {
             const measureStartLine: SystemLinesEnum = this.getMeasureStartLine();
             currentMeasureBeginInstructionsWidth += this.getLineWidth(graphicalMeasures[0], measureStartLine, isSystemStartMeasure);
             if (!this.leadSheet) {
-                currentMeasureBeginInstructionsWidth += this.addBeginInstructions(graphicalMeasures, isSystemStartMeasure, isFirstSourceMeasure);
+                let forceShowRhythm: boolean = false;
+                if (prevMeasureEndsPart && this.rules.ShowRhythmAgainAfterPartEndOrFinalBarline) {
+                    forceShowRhythm = true;
+                }
+                currentMeasureBeginInstructionsWidth += this.addBeginInstructions(  graphicalMeasures,
+                                                                                    isSystemStartMeasure,
+                                                                                    isFirstSourceMeasure || forceShowRhythm);
+                // forceShowRhythm could be a fourth parameter instead in addBeginInstructions, but only affects RhythmInstruction for now.
                 currentMeasureEndInstructionsWidth += this.addEndInstructions(graphicalMeasures);
             }
             let currentMeasureVarWidth: number = 0;
@@ -116,30 +117,70 @@ 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;
-                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;
-            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
                 );
                 this.updateActiveClefs(sourceMeasure, graphicalMeasures);
                 this.measureListIndex++;
+                if (sourceMeasureBreaksSystem) {
+                    if (this.rules.MaxSystemToDrawNumber === this.musicSystems.length) {
+                        this.finalizeCurrentSystem(graphicalMeasures, !this.rules.StretchLastSystemLine, false);
+                        return this.musicSystems;
+                    }
+                    this.finalizeCurrentAndCreateNewSystem(graphicalMeasures, !this.rules.StretchLastSystemLine, false);
+                }
+                prevMeasureEndsPart = sourceMeasureEndsPart;
             } else {
+                if (this.rules.MaxSystemToDrawNumber === this.musicSystems.length) {
+                    this.finalizeCurrentSystem(graphicalMeasures, false, true, doXmlPageBreak);
+                    return this.musicSystems;
+                }
                 // finalize current system and prepare a new one
-                this.finalizeCurrentAndCreateNewSystem(graphicalMeasures, previousMeasureEndsSystem);
+                this.finalizeCurrentAndCreateNewSystem(graphicalMeasures, false, true, doXmlPageBreak);
                 // don't increase measure index to check this measure now again
+                // don't set prevMeasureEndsPart in this case! because we will loop with the same measure again.
+            }
+        }
+        if (this.currentSystemParams.systemMeasures.length > 0) {
+            if (this.rules.MaxSystemToDrawNumber === this.musicSystems.length) {
+                this.finalizeCurrentSystem(this.measureList[this.measureList.length - 1], !this.rules.StretchLastSystemLine, false);
+                return this.musicSystems;
             }
-            previousMeasureEndsSystem = sourceMeasureEndsSystem;
+            this.finalizeCurrentAndCreateNewSystem(this.measureList[this.measureList.length - 1], !this.rules.StretchLastSystemLine, false);
+        }
+        return this.musicSystems;
+    }
+
+    /**
+     * calculates the y positions of the staff lines within a system and
+     * furthermore the y positions of the systems themselves.
+     */
+    public calculateSystemYLayout(): void {
+        for (const musicSystem of this.musicSystems) {
+            this.optimizeDistanceBetweenStaffLines(musicSystem);
         }
-        this.finalizeCurrentAndCreateNewSystem(this.measureList[this.measureList.length - 1], true);
+
+        // set y positions of systems using the previous system and a fixed distance.
+        this.calculateMusicSystemsRelativePositions();
     }
 
     /**
@@ -149,7 +190,7 @@ export class MusicSystemBuilder {
      * @param beginInstrWidth
      * @param endInstrWidth
      */
-    private setMeasureWidth(graphicalMeasures: GraphicalMeasure[], width: number, beginInstrWidth: number, endInstrWidth: number): void {
+    protected setMeasureWidth(graphicalMeasures: GraphicalMeasure[], width: number, beginInstrWidth: number, endInstrWidth: number): void {
         for (let idx: number = 0, len: number = graphicalMeasures.length; idx < len; ++idx) {
             const measure: GraphicalMeasure = graphicalMeasures[idx];
             measure.setWidth(width);
@@ -168,30 +209,29 @@ export class MusicSystemBuilder {
      * @param measures
      * @param isPartEndingSystem
      */
-    private finalizeCurrentAndCreateNewSystem(measures: GraphicalMeasure[], isPartEndingSystem: boolean = false): void {
+    protected finalizeCurrentAndCreateNewSystem(measures: GraphicalMeasure[],
+                                                systemEndsPart: boolean = false,
+                                                checkExtraInstructionMeasure: boolean = true,
+                                                startNewPage: boolean = false): void {
+        this.finalizeCurrentSystem(measures, systemEndsPart, checkExtraInstructionMeasure, startNewPage);
+        this.currentSystemParams = new SystemBuildParameters(); // this and the following used to be in finalizeCurrentSystem after stretchMusicSystem
+        if (measures !== undefined &&
+            this.measureListIndex < this.measureList.length) {
+            this.currentSystemParams.currentSystem = this.initMusicSystem();
+        }
+    }
+
+    protected finalizeCurrentSystem(measures: GraphicalMeasure[],
+                                    systemEndsPart: boolean = false,
+                                    checkExtraInstructionMeasure: boolean = true,
+                                    startNewPage: boolean = false): void {
+        this.currentSystemParams.currentSystem.breaksPage = startNewPage;
         this.adaptRepetitionLineWithIfNeeded();
-        if (!isPartEndingSystem) {
+        if (measures !== undefined &&
+            checkExtraInstructionMeasure) {
             this.checkAndCreateExtraInstructionMeasure(measures);
         }
-        this.stretchMusicSystem(isPartEndingSystem);
-        if (this.currentPageHeight + this.currentSystemParams.currentSystem.PositionAndShape.Size.height + this.rules.SystemDistance <= this.rules.PageHeight) {
-            this.currentPageHeight += this.currentSystemParams.currentSystem.PositionAndShape.Size.height + this.rules.SystemDistance;
-            if (
-                this.currentPageHeight + this.currentSystemParams.currentSystem.PositionAndShape.Size.height
-                + this.rules.SystemDistance >= this.rules.PageHeight
-            ) {
-                this.currentMusicPage = this.createMusicPage();
-                this.currentPageHeight = this.rules.PageTopMargin + this.rules.TitleTopDistance;
-            }
-        } else {
-            this.currentMusicPage = this.createMusicPage();
-            this.currentPageHeight = this.rules.PageTopMargin + this.rules.TitleTopDistance;
-        }
-        this.currentSystemParams = new SystemBuildParameters();
-        if (this.measureListIndex < this.measureList.length) {
-            this.currentSystemParams.currentSystem = this.initMusicSystem();
-            this.layoutSystemStaves();
-        }
+        this.stretchMusicSystem(systemEndsPart);
     }
 
     /**
@@ -200,7 +240,7 @@ export class MusicSystemBuilder {
      * one at the next system.
      * (this should be refactored at some point to not use a combined end/start line but always separated lines)
      */
-    private adaptRepetitionLineWithIfNeeded(): void {
+    protected adaptRepetitionLineWithIfNeeded(): void {
         const systemMeasures: MeasureBuildParameters[] = this.currentSystemParams.systemMeasures;
         if (systemMeasures.length >= 1) {
             const measures: GraphicalMeasure[] =
@@ -219,7 +259,7 @@ export class MusicSystemBuilder {
         }
     }
 
-    private addMeasureToSystem(
+    protected addMeasureToSystem(
         graphicalMeasures: GraphicalMeasure[], measureStartLine: SystemLinesEnum, measureEndLine: SystemLinesEnum,
         totalMeasureWidth: number, currentMeasureBeginInstructionsWidth: number, currentVarWidth: number, currentMeasureEndInstructionsWidth: number
     ): void {
@@ -235,28 +275,19 @@ export class MusicSystemBuilder {
     }
 
     /**
-     * Create a new [[GraphicalMusicPage]]
-     * (for now only one long page is used per music sheet, as we scroll down and have no page flips)
-     * @returns {GraphicalMusicPage}
-     */
-    private createMusicPage(): GraphicalMusicPage {
-        const page: GraphicalMusicPage = new GraphicalMusicPage(this.graphicalMusicSheet);
-        this.graphicalMusicSheet.MusicPages.push(page);
-        page.PositionAndShape.BorderLeft = 0.0;
-        page.PositionAndShape.BorderRight = this.graphicalMusicSheet.ParentMusicSheet.pageWidth;
-        page.PositionAndShape.BorderTop = 0.0;
-        page.PositionAndShape.BorderBottom = this.rules.PageHeight;
-        page.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
-        return page;
-    }
-
-    /**
      * Initialize a new [[MusicSystem]].
      * @returns {MusicSystem}
      */
-    private initMusicSystem(): MusicSystem {
-        const musicSystem: MusicSystem = MusicSheetCalculator.symbolFactory.createMusicSystem(this.currentMusicPage, this.globalSystemIndex++);
-        this.currentMusicPage.MusicSystems.push(musicSystem);
+    protected initMusicSystem(): MusicSystem {
+        const musicSystem: MusicSystem = MusicSheetCalculator.symbolFactory.createMusicSystem(this.globalSystemIndex++, this.rules);
+        this.musicSystems.push(musicSystem);
+        this.layoutSystemStaves(musicSystem);
+        musicSystem.createMusicSystemLabel(
+            this.rules.InstrumentLabelTextHeight,
+            this.rules.SystemLabelsRightMargin,
+            this.rules.LabelMarginBorderFactor,
+            this.musicSystems.length === 1
+        );
         return musicSystem;
     }
 
@@ -264,14 +295,13 @@ export class MusicSystemBuilder {
      * Get the width the system should have for a given page width.
      * @returns {number}
      */
-    private getFullPageSystemWidth(): number {
-        return this.currentMusicPage.PositionAndShape.Size.width - this.rules.PageLeftMargin
+    protected getFullPageSystemWidth(): number {
+        return this.graphicalMusicSheet.ParentMusicSheet.pageWidth - this.rules.PageLeftMargin
             - this.rules.PageRightMargin - this.rules.SystemLeftMargin - this.rules.SystemRightMargin;
     }
 
-    private layoutSystemStaves(): void {
+    protected layoutSystemStaves(musicSystem: MusicSystem): void {
         const systemWidth: number = this.getFullPageSystemWidth();
-        const musicSystem: MusicSystem = this.currentSystemParams.currentSystem;
         const boundingBox: BoundingBox = musicSystem.PositionAndShape;
         boundingBox.BorderLeft = 0.0;
         boundingBox.BorderRight = systemWidth;
@@ -280,7 +310,7 @@ export class MusicSystemBuilder {
         const instruments: Instrument[] = this.graphicalMusicSheet.ParentMusicSheet.Instruments;
         for (let idx: number = 0, len: number = instruments.length; idx < len; ++idx) {
             const instrument: Instrument = instruments[idx];
-            if (instrument.Voices.length === 0 || !instrument.Visible) {
+            if (!instrument.Visible || instrument.Voices.length === 0) {
                 continue;
             }
             for (let idx2: number = 0, len2: number = instrument.Staves.length; idx2 < len2; ++idx2) {
@@ -325,21 +355,14 @@ export class MusicSystemBuilder {
      * @param relativeYPosition
      * @param staff
      */
-    private addStaffLineToMusicSystem(musicSystem: MusicSystem, relativeYPosition: number, staff: Staff): void {
-        if (musicSystem !== undefined) {
+    protected addStaffLineToMusicSystem(musicSystem: MusicSystem, relativeYPosition: number, staff: Staff): void {
+        if (musicSystem) {
             const staffLine: StaffLine = MusicSheetCalculator.symbolFactory.createStaffLine(musicSystem, staff);
             musicSystem.StaffLines.push(staffLine);
             const boundingBox: BoundingBox = staffLine.PositionAndShape;
             const relativePosition: PointF2D = new PointF2D();
-            if (musicSystem.Parent.MusicSystems[0] === musicSystem &&
-                musicSystem.Parent === musicSystem.Parent.Parent.MusicPages[0] &&
-                !EngravingRules.Rules.CompactMode) {
-                relativePosition.x = this.rules.FirstSystemMargin;
-                boundingBox.BorderRight = musicSystem.PositionAndShape.Size.width - this.rules.FirstSystemMargin;
-            } else {
-                relativePosition.x = 0.0;
-                boundingBox.BorderRight = musicSystem.PositionAndShape.Size.width;
-            }
+            relativePosition.x = 0.0;
+            boundingBox.BorderRight = musicSystem.PositionAndShape.Size.width;
             relativePosition.y = relativeYPosition;
             boundingBox.RelativePosition = relativePosition;
             boundingBox.BorderLeft = 0.0;
@@ -364,25 +387,30 @@ export class MusicSystemBuilder {
      * Initialize the active Instructions from the first [[SourceMeasure]] of first [[SourceMusicPart]].
      * @param measureList
      */
-    private initializeActiveInstructions(measureList: GraphicalMeasure[]): void {
+    protected initializeActiveInstructions(measureList: GraphicalMeasure[]): void {
         const firstSourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.getFirstSourceMeasure();
-        if (firstSourceMeasure !== undefined) {
-            this.visibleStaffIndices = this.graphicalMusicSheet.getVisibleStavesIndecesFromSourceMeasure(measureList);
+        if (firstSourceMeasure) {
+            this.visibleStaffIndices = this.graphicalMusicSheet.getVisibleStavesIndicesFromSourceMeasure(measureList);
             for (let i: number = 0, len: number = this.visibleStaffIndices.length; i < len; i++) {
                 const staffIndex: number = this.visibleStaffIndices[i];
                 const graphicalMeasure: GraphicalMeasure = this.graphicalMusicSheet
                     .getGraphicalMeasureFromSourceMeasureAndIndex(firstSourceMeasure, staffIndex);
                 this.activeClefs[i] = <ClefInstruction>firstSourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions[0];
-                let keyInstruction: KeyInstruction = KeyInstruction.copy(
-                    <KeyInstruction>firstSourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions[1]);
-                keyInstruction = this.transposeKeyInstruction(keyInstruction, graphicalMeasure);
-                this.activeKeys[i] = keyInstruction;
-                this.activeRhythm[i] = <RhythmInstruction>firstSourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions[2];
+                const firstKeyInstruction: KeyInstruction = <KeyInstruction>firstSourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions[1];
+                if (firstKeyInstruction) {
+                    let keyInstruction: KeyInstruction = KeyInstruction.copy(firstKeyInstruction);
+                    keyInstruction = this.transposeKeyInstruction(keyInstruction, graphicalMeasure);
+                    this.activeKeys[i] = keyInstruction;
+                }
+                const firstRhythmInstruction: RhythmInstruction = <RhythmInstruction>
+                    firstSourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions[2];
+                // if (firstRhythmInstruction) {
+                this.activeRhythm[i] = firstRhythmInstruction;
             }
         }
     }
 
-    private transposeKeyInstruction(keyInstruction: KeyInstruction, graphicalMeasure: GraphicalMeasure): KeyInstruction {
+    protected transposeKeyInstruction(keyInstruction: KeyInstruction, graphicalMeasure: GraphicalMeasure): KeyInstruction {
         if (this.graphicalMusicSheet.ParentMusicSheet.Transpose !== 0
             && graphicalMeasure.ParentStaff.ParentInstrument.MidiInstrumentId !== MidiInstrument.Percussion
             && MusicSheetCalculator.transposeCalculator !== undefined
@@ -402,7 +430,7 @@ export class MusicSystemBuilder {
      * @param isFirstSourceMeasure
      * @returns {number}
      */
-    private addBeginInstructions(measures: GraphicalMeasure[], isSystemFirstMeasure: boolean, isFirstSourceMeasure: boolean): number {
+    protected addBeginInstructions(measures: GraphicalMeasure[], isSystemFirstMeasure: boolean, isFirstSourceMeasure: boolean): number {
         const measureCount: number = measures.length;
         if (measureCount === 0) {
             return 0;
@@ -428,7 +456,7 @@ export class MusicSystemBuilder {
      * @param measures
      * @returns {number}
      */
-    private addEndInstructions(measures: GraphicalMeasure[]): number {
+    protected addEndInstructions(measures: GraphicalMeasure[]): number {
         const measureCount: number = measures.length;
         if (measureCount === 0) {
             return 0;
@@ -445,13 +473,13 @@ export class MusicSystemBuilder {
         return totalEndInstructionLengthX;
     }
 
-    private AddInstructionsAtMeasureBegin(firstEntry: SourceStaffEntry, measure: GraphicalMeasure,
-                                          visibleStaffIdx: number, isFirstSourceMeasure: boolean, isSystemStartMeasure: boolean): number {
+    protected AddInstructionsAtMeasureBegin(firstEntry: SourceStaffEntry, measure: GraphicalMeasure,
+                                            visibleStaffIdx: number, isFirstSourceMeasure: boolean, isSystemStartMeasure: boolean): number {
         let instructionsLengthX: number = 0;
         let currentClef: ClefInstruction = undefined;
         let currentKey: KeyInstruction = undefined;
         let currentRhythm: RhythmInstruction = undefined;
-        if (firstEntry !== undefined) {
+        if (firstEntry) {
             for (let idx: number = 0, len: number = firstEntry.Instructions.length; idx < len; ++idx) {
                 const abstractNotationInstruction: AbstractNotationInstruction = firstEntry.Instructions[idx];
                 if (abstractNotationInstruction instanceof ClefInstruction) {
@@ -464,34 +492,46 @@ export class MusicSystemBuilder {
             }
         }
         if (isSystemStartMeasure) {
-            if (currentClef === undefined) {
+            if (!currentClef) {
                 currentClef = this.activeClefs[visibleStaffIdx];
             }
-            if (currentKey === undefined) {
+            if (!currentKey) {
                 currentKey = this.activeKeys[visibleStaffIdx];
             }
-            if (isFirstSourceMeasure && currentRhythm === undefined) {
+            if (isFirstSourceMeasure && !currentRhythm) {
                 currentRhythm = this.activeRhythm[visibleStaffIdx];
             }
         }
         let clefAdded: boolean = false;
         let keyAdded: boolean = false;
         let rhythmAdded: boolean = false;
-        if (currentClef !== undefined) {
+        if (currentClef) {
             measure.addClefAtBegin(currentClef);
             clefAdded = true;
         } else {
             currentClef = this.activeClefs[visibleStaffIdx];
         }
-        if (currentKey !== undefined) {
+        if (currentKey) {
             currentKey = this.transposeKeyInstruction(currentKey, measure);
             const previousKey: KeyInstruction = isSystemStartMeasure ? undefined : this.activeKeys[visibleStaffIdx];
             measure.addKeyAtBegin(currentKey, previousKey, currentClef);
             keyAdded = true;
         }
-        if (currentRhythm !== undefined && currentRhythm.PrintObject) {
-            measure.addRhythmAtBegin(currentRhythm);
-            rhythmAdded = true;
+        if (currentRhythm !== undefined && currentRhythm.PrintObject && this.rules.RenderTimeSignatures) {
+            let printRhythm: boolean = true;
+            // check for previous pickup measure
+            const pickupMeasureNumber: number = measure.MeasureNumber - 1;
+            if (measure.MeasureNumber - 1 >= 0) {
+                const previousMeasure: SourceMeasure = this.measureList[pickupMeasureNumber][0]?.parentSourceMeasure;
+                if (previousMeasure?.ImplicitMeasure && previousMeasure?.RhythmPrinted) {
+                    printRhythm = false;
+                }
+            }
+            if (printRhythm) {
+                measure.addRhythmAtBegin(currentRhythm);
+                measure.parentSourceMeasure.RhythmPrinted = true;
+                rhythmAdded = true;
+            }
         }
         if (clefAdded || keyAdded || rhythmAdded) {
             instructionsLengthX += measure.beginInstructionsWidth;
@@ -502,8 +542,8 @@ export class MusicSystemBuilder {
         return instructionsLengthX;
     }
 
-    private addInstructionsAtMeasureEnd(lastEntry: SourceStaffEntry, measure: GraphicalMeasure): number {
-        if (lastEntry === undefined || lastEntry.Instructions === undefined || lastEntry.Instructions.length === 0) {
+    protected addInstructionsAtMeasureEnd(lastEntry: SourceStaffEntry, measure: GraphicalMeasure): number {
+        if (!lastEntry || !lastEntry.Instructions || lastEntry.Instructions.length === 0) {
             return 0;
         }
         for (let idx: number = 0, len: number = lastEntry.Instructions.length; idx < len; ++idx) {
@@ -523,11 +563,11 @@ export class MusicSystemBuilder {
      * @param measure
      * @param graphicalMeasures
      */
-    private updateActiveClefs(measure: SourceMeasure, graphicalMeasures: GraphicalMeasure[]): void {
+    protected updateActiveClefs(measure: SourceMeasure, graphicalMeasures: GraphicalMeasure[]): void {
         for (let visStaffIdx: number = 0, len: number = graphicalMeasures.length; visStaffIdx < len; visStaffIdx++) {
             const staffIndex: number = this.visibleStaffIndices[visStaffIdx];
             const firstEntry: SourceStaffEntry = measure.FirstInstructionsStaffEntries[staffIndex];
-            if (firstEntry !== undefined) {
+            if (firstEntry) {
                 for (let idx: number = 0, len2: number = firstEntry.Instructions.length; idx < len2; ++idx) {
                     const abstractNotationInstruction: AbstractNotationInstruction = firstEntry.Instructions[idx];
                     if (abstractNotationInstruction instanceof ClefInstruction) {
@@ -542,7 +582,7 @@ export class MusicSystemBuilder {
             const entries: SourceStaffEntry[] = measure.getEntriesPerStaff(staffIndex);
             for (let idx: number = 0, len2: number = entries.length; idx < len2; ++idx) {
                 const staffEntry: SourceStaffEntry = entries[idx];
-                if (staffEntry.Instructions !== undefined) {
+                if (staffEntry.Instructions) {
                     for (let idx2: number = 0, len3: number = staffEntry.Instructions.length; idx2 < len3; ++idx2) {
                         const abstractNotationInstruction: AbstractNotationInstruction = staffEntry.Instructions[idx2];
                         if (abstractNotationInstruction instanceof ClefInstruction) {
@@ -552,7 +592,7 @@ export class MusicSystemBuilder {
                 }
             }
             const lastEntry: SourceStaffEntry = measure.LastInstructionsStaffEntries[staffIndex];
-            if (lastEntry !== undefined) {
+            if (lastEntry) {
                 const instructions: AbstractNotationInstruction[] = lastEntry.Instructions;
                 for (let idx: number = 0, len3: number = instructions.length; idx < len3; ++idx) {
                     const abstractNotationInstruction: AbstractNotationInstruction = instructions[idx];
@@ -568,7 +608,7 @@ export class MusicSystemBuilder {
      * Check if an extra Instruction [[Measure]] is needed.
      * @param measures
      */
-    private checkAndCreateExtraInstructionMeasure(measures: GraphicalMeasure[]): void {
+    protected checkAndCreateExtraInstructionMeasure(measures: GraphicalMeasure[]): void {
         const firstStaffEntries: SourceStaffEntry[] = measures[0].parentSourceMeasure.FirstInstructionsStaffEntries;
         const visibleInstructionEntries: SourceStaffEntry[] = [];
         for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
@@ -578,7 +618,7 @@ export class MusicSystemBuilder {
         let maxMeasureWidth: number = 0;
         for (let visStaffIdx: number = 0, len: number = visibleInstructionEntries.length; visStaffIdx < len; ++visStaffIdx) {
             const sse: SourceStaffEntry = visibleInstructionEntries[visStaffIdx];
-            if (sse === undefined) {
+            if (!sse) {
                 continue;
             }
             const instructions: AbstractNotationInstruction[] = sse.Instructions;
@@ -593,7 +633,7 @@ export class MusicSystemBuilder {
                     rhythmInstruction = <RhythmInstruction>instruction;
                 }
             }
-            if (keyInstruction !== undefined || rhythmInstruction !== undefined) {
+            if (keyInstruction !== undefined || rhythmInstruction) {
                 const measureWidth: number = this.addExtraInstructionMeasure(visStaffIdx, keyInstruction, rhythmInstruction);
                 maxMeasureWidth = Math.max(maxMeasureWidth, measureWidth);
             }
@@ -608,12 +648,12 @@ export class MusicSystemBuilder {
         }
     }
 
-    private addExtraInstructionMeasure(visStaffIdx: number, keyInstruction: KeyInstruction, rhythmInstruction: RhythmInstruction): number {
+    protected addExtraInstructionMeasure(visStaffIdx: number, keyInstruction: KeyInstruction, rhythmInstruction: RhythmInstruction): number {
         const currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
         const measures: GraphicalMeasure[] = [];
         const measure: GraphicalMeasure = MusicSheetCalculator.symbolFactory.createExtraGraphicalMeasure(currentSystem.StaffLines[visStaffIdx]);
         measures.push(measure);
-        if (keyInstruction !== undefined) {
+        if (keyInstruction) {
             measure.addKeyAtBegin(keyInstruction, this.activeKeys[visStaffIdx], this.activeClefs[visStaffIdx]);
         }
         if (rhythmInstruction !== undefined && rhythmInstruction.PrintObject) {
@@ -632,8 +672,8 @@ export class MusicSystemBuilder {
      * Add all current vertical Measures to currentSystem.
      * @param graphicalMeasures
      */
-    private addStaveMeasuresToSystem(graphicalMeasures: GraphicalMeasure[]): void {
-        if (graphicalMeasures[0] !== undefined) {
+    protected addStaveMeasuresToSystem(graphicalMeasures: GraphicalMeasure[]): void {
+        if (graphicalMeasures[0]) {
             const gmeasures: GraphicalMeasure[] = [];
             for (let i: number = 0; i < graphicalMeasures.length; i++) {
                 gmeasures.push(graphicalMeasures[i]);
@@ -652,7 +692,7 @@ export class MusicSystemBuilder {
      * Return the width of the corresponding [[SystemLine]] and set the corresponding [[SystemLineEnum]].
      * @returns {SystemLinesEnum}
      */
-    private getMeasureStartLine(): SystemLinesEnum {
+    protected getMeasureStartLine(): SystemLinesEnum {
         const thisMeasureBeginsLineRep: boolean = this.thisMeasureBeginsLineRepetition();
         if (thisMeasureBeginsLineRep) {
             const isSystemStartMeasure: boolean = this.currentSystemParams.IsSystemStartMeasure();
@@ -667,19 +707,46 @@ export class MusicSystemBuilder {
         return SystemLinesEnum.None;
     }
 
-    private getMeasureEndLine(): SystemLinesEnum {
+    protected getMeasureEndLine(): SystemLinesEnum {
+        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
+        }
+
         if (this.nextMeasureBeginsLineRepetition() && this.thisMeasureEndsLineRepetition()) {
             return SystemLinesEnum.DotsBoldBoldDots;
         }
         if (this.thisMeasureEndsLineRepetition()) {
             return SystemLinesEnum.DotsThinBold;
         }
-        if (this.measureListIndex === this.measureList.length - 1 || this.measureList[this.measureListIndex][0].parentSourceMeasure.endsPiece) {
+        // always end piece with final barline: not a good idea. user should be able to override final barline.
+        // also, selecting range of measures to draw would always end with final barline, even if extract is from the middle of the piece
+        // this was probably done before we parsed the barline type from XML.
+        /*if (this.measureListIndex === this.measureList.length - 1 || this.measureList[this.measureListIndex][0].parentSourceMeasure.endsPiece) {
             return SystemLinesEnum.ThinBold;
-        }
+        }*/
         if (this.nextMeasureHasKeyInstructionChange() || this.thisMeasureEndsWordRepetition() || this.nextMeasureBeginsWordRepetition()) {
             return SystemLinesEnum.DoubleThin;
         }
+        if (!sourceMeasure) {
+            return SystemLinesEnum.SingleThin;
+        }
+        if (sourceMeasure.endingBarStyleEnum !== undefined) {
+            return sourceMeasure.endingBarStyleEnum;
+        }
+        // TODO: print an error message if the default fallback is used.
         return SystemLinesEnum.SingleThin;
     }
 
@@ -690,7 +757,7 @@ export class MusicSystemBuilder {
      * @param isSystemStartMeasure
      * @returns {number}
      */
-    private getLineWidth(measure: GraphicalMeasure, systemLineEnum: SystemLinesEnum, isSystemStartMeasure: boolean): number {
+    protected getLineWidth(measure: GraphicalMeasure, systemLineEnum: SystemLinesEnum, isSystemStartMeasure: boolean): number {
         let width: number = measure.getLineWidth(systemLineEnum);
         if (systemLineEnum === SystemLinesEnum.DotsBoldBoldDots) {
             width /= 2;
@@ -701,7 +768,7 @@ export class MusicSystemBuilder {
         return width;
     }
 
-    private previousMeasureEndsLineRepetition(): boolean {
+    protected previousMeasureEndsLineRepetition(): boolean {
         if (this.measureListIndex === 0) {
             return false;
         }
@@ -718,7 +785,7 @@ export class MusicSystemBuilder {
      * Check if at this [[Measure]] starts a [[Repetition]].
      * @returns {boolean}
      */
-    private thisMeasureBeginsLineRepetition(): boolean {
+    protected thisMeasureBeginsLineRepetition(): boolean {
         for (let idx: number = 0, len: number = this.measureList[this.measureListIndex].length; idx < len; ++idx) {
             const measure: GraphicalMeasure = this.measureList[this.measureListIndex][idx];
             if (measure.beginsWithLineRepetition()) {
@@ -732,7 +799,7 @@ export class MusicSystemBuilder {
      * Check if a [[Repetition]] starts at the next [[Measure]].
      * @returns {boolean}
      */
-    private nextMeasureBeginsLineRepetition(): boolean {
+    protected nextMeasureBeginsLineRepetition(): boolean {
         const nextMeasureIndex: number = this.measureListIndex + 1;
         if (nextMeasureIndex >= this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length
             || !this.measureList[nextMeasureIndex]) {
@@ -751,7 +818,7 @@ export class MusicSystemBuilder {
      * Check if this [[Measure]] is a [[Repetition]] ending.
      * @returns {boolean}
      */
-    private thisMeasureEndsLineRepetition(): boolean {
+    protected thisMeasureEndsLineRepetition(): boolean {
         for (let idx: number = 0, len: number = this.measureList[this.measureListIndex].length; idx < len; ++idx) {
             const measure: GraphicalMeasure = this.measureList[this.measureListIndex][idx];
             if (measure.endsWithLineRepetition()) {
@@ -765,9 +832,10 @@ export class MusicSystemBuilder {
      * Check if a [[Repetition]] starts at the next [[Measure]].
      * @returns {boolean}
      */
-    private nextMeasureBeginsWordRepetition(): boolean {
+    protected nextMeasureBeginsWordRepetition(): boolean {
         const nextMeasureIndex: number = this.measureListIndex + 1;
-        if (nextMeasureIndex >= this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length) {
+        if (nextMeasureIndex >= this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length ||
+            nextMeasureIndex > this.measureList.length - 1) {
             return false;
         }
         for (let idx: number = 0, len: number = this.measureList[nextMeasureIndex].length; idx < len; ++idx) {
@@ -783,7 +851,7 @@ export class MusicSystemBuilder {
      * Check if this [[Measure]] is a [[Repetition]] ending.
      * @returns {boolean}
      */
-    private thisMeasureEndsWordRepetition(): boolean {
+    protected thisMeasureEndsWordRepetition(): boolean {
         for (let idx: number = 0, len: number = this.measureList[this.measureListIndex].length; idx < len; ++idx) {
             const measure: GraphicalMeasure = this.measureList[this.measureListIndex][idx];
             if (measure.endsWithWordRepetition()) {
@@ -797,15 +865,15 @@ export class MusicSystemBuilder {
      * Check if the next [[Measure]] has a [[KeyInstruction]] change.
      * @returns {boolean}
      */
-    private nextMeasureHasKeyInstructionChange(): boolean {
+    protected nextMeasureHasKeyInstructionChange(): boolean {
         return this.getNextMeasureKeyInstruction() !== undefined;
     }
 
-    private getNextMeasureKeyInstruction(): KeyInstruction {
+    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;
-                if (sourceMeasure === undefined) {
+                const sourceMeasure: SourceMeasure = this.measureList[this.measureListIndex + 1][visIndex]?.parentSourceMeasure;
+                if (!sourceMeasure) {
                     return undefined;
                 }
                 return sourceMeasure.getKeyInstruction(this.visibleStaffIndices[visIndex]);
@@ -820,7 +888,7 @@ export class MusicSystemBuilder {
      * @param systemVarWidth
      * @returns {number}
      */
-    private calculateXScalingFactor(systemFixWidth: number, systemVarWidth: number): number {
+    protected calculateXScalingFactor(systemFixWidth: number, systemVarWidth: number): number {
         if (Math.abs(systemVarWidth - 0) < 0.00001 || Math.abs(systemFixWidth - 0) < 0.00001) {
             return 1.0;
         }
@@ -833,13 +901,13 @@ export class MusicSystemBuilder {
 
     /**
      * Stretch the whole System so that no white space is left at the end.
-     * @param isPartEndingSystem
+     * @param systemEndsPart
      */
-    private stretchMusicSystem(isPartEndingSystem: boolean): void {
+    protected stretchMusicSystem(systemEndsPart: boolean): void {
         let scalingFactor: number = this.calculateXScalingFactor(
             this.currentSystemParams.currentSystemFixWidth, this.currentSystemParams.currentSystemVarWidth
         );
-        if (isPartEndingSystem) {
+        if (systemEndsPart) {
             scalingFactor = Math.min(scalingFactor, this.rules.LastSystemMaxScalingFactor);
         }
         const currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
@@ -849,7 +917,13 @@ export class MusicSystemBuilder {
             for (let measureIndex: number = 0; measureIndex < staffLine.Measures.length; measureIndex++) {
                 const measure: GraphicalMeasure = staffLine.Measures[measureIndex];
                 measure.setPositionInStaffline(currentXPosition);
-                measure.setWidth(measure.beginInstructionsWidth + measure.minimumStaffEntriesWidth * scalingFactor + measure.endInstructionsWidth);
+                const beginInstructionsWidth: number = measure.beginInstructionsWidth;
+                // if (measureIndex === 0 && measure.staffEntries) {
+                //     if (!measure.parentSourceMeasure.hasLyrics) {
+                //         beginInstructionsWidth *= 1; // TODO the first measure in a system is always slightly too big. why? try e.g. 0.6
+                //     }
+                // }
+                measure.setWidth(beginInstructionsWidth + measure.minimumStaffEntriesWidth * scalingFactor + measure.endInstructionsWidth);
                 if (measureIndex < this.currentSystemParams.systemMeasures.length) {
                     const startLine: SystemLinesEnum = this.currentSystemParams.systemMeasures[measureIndex].beginLine;
                     const lineWidth: number = measure.getLineWidth(SystemLinesEnum.BoldThinDots);
@@ -884,7 +958,7 @@ export class MusicSystemBuilder {
                 currentXPosition = measure.PositionAndShape.RelativePosition.x + measure.PositionAndShape.BorderRight;
             }
         }
-        if (isPartEndingSystem) {
+        if (systemEndsPart) {
             this.decreaseMusicSystemBorders();
         }
     }
@@ -893,7 +967,7 @@ export class MusicSystemBuilder {
      * If the last [[MusicSystem]] doesn't need stretching, then this method decreases the System's Width,
      * the [[StaffLine]]'s Width and the 5 [[StaffLine]]s length.
      */
-    private decreaseMusicSystemBorders(): void {
+    protected decreaseMusicSystemBorders(): void {
         const currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
         const bb: BoundingBox = CollectionUtil.last(currentSystem.StaffLines[0].Measures).PositionAndShape;
         const width: number = bb.RelativePosition.x + bb.Size.width;
@@ -907,7 +981,264 @@ export class MusicSystemBuilder {
         }
         currentSystem.PositionAndShape.BorderRight = width + this.currentSystemParams.maxLabelLength + this.rules.SystemLabelsRightMargin;
     }
+
+    /**
+     * This method updates the System's StaffLine's RelativePosition (starting from the given index).
+     * @param musicSystem
+     * @param index
+     * @param value
+     */
+    protected updateStaffLinesRelativePosition(musicSystem: MusicSystem, index: number, value: number): void {
+        for (let i: number = index; i < musicSystem.StaffLines.length; i++) {
+            musicSystem.StaffLines[i].PositionAndShape.RelativePosition.y = value;
+        }
+
+        musicSystem.PositionAndShape.BorderBottom += value;
+    }
+
+    /**
+     * Create a new [[GraphicalMusicPage]]
+     * (for now only one long page is used per music sheet, as we scroll down and have no page flips)
+     * @returns {GraphicalMusicPage}
+     */
+    protected createMusicPage(): GraphicalMusicPage {
+        // const previousPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages.last();
+        // const previousSizeY: number = previousPage ? previousPage.PositionAndShape.Size.height : 0;
+        const page: GraphicalMusicPage = new GraphicalMusicPage(this.graphicalMusicSheet);
+        this.graphicalMusicSheet.MusicPages.push(page);
+        page.PageNumber = this.graphicalMusicSheet.MusicPages.length; // caution: page number = page index + 1
+        page.PositionAndShape.BorderLeft = 0.0;
+        page.PositionAndShape.BorderRight = this.graphicalMusicSheet.ParentMusicSheet.pageWidth;
+        page.PositionAndShape.BorderTop = 0.0;
+        page.PositionAndShape.BorderBottom = this.rules.PageHeight;
+        page.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
+        return page;
+    }
+
+    protected addSystemToPage(page: GraphicalMusicPage, system: MusicSystem): void {
+        page.MusicSystems.push(system);
+        system.Parent = page;
+    }
+
+    /**
+     * This method checks the distances between any two consecutive StaffLines of a System and if needed, shifts the lower one down.
+     * @param musicSystem
+     */
+    protected optimizeDistanceBetweenStaffLines(musicSystem: MusicSystem): void {
+        // don't perform any y-spacing in case of a StaffEntryLink (in both StaffLines)
+        if (!musicSystem.checkStaffEntriesForStaffEntryLink()) {
+            for (let i: number = 0; i < musicSystem.StaffLines.length - 1; i++) {
+                const upperBottomLine: number[] = musicSystem.StaffLines[i].BottomLine;
+                const lowerSkyLine: number[] = musicSystem.StaffLines[i + 1].SkyLine;
+                // 1. Find maximum required space for sky bottom line touching each other
+                let maxDistance: number = 0;
+                for (let j: number = 0; j < upperBottomLine.length; j++) {
+                    const bottomLineValue: number = upperBottomLine[j];
+
+                    // look at a range of +/- 2 Units to also ensure that objects are also not too close in x-direction:
+                    const startIdx: number = Math.max(0, j - 6);
+                    const endIdx: number = Math.min(lowerSkyLine.length - 1, j + 6);
+                    let skylineValue: number = 0;
+                    for (let lowerIdx: number = startIdx; lowerIdx <= endIdx; lowerIdx++) {
+                        skylineValue = Math.min(skylineValue, lowerSkyLine[lowerIdx]);
+                    }
+
+                    const distance: number = bottomLineValue - skylineValue;
+                    maxDistance = Math.max(distance, maxDistance);
+                }
+                // 2. Add user defined distance between sky bottom line
+                maxDistance += this.rules.MinSkyBottomDistBetweenStaves;
+                // 3. Take the maximum between previous value and user defined value for staff line minimum distance
+                maxDistance = Math.max(maxDistance, this.rules.StaffHeight + this.rules.MinimumStaffLineDistance);
+                const lowerStafflineYPos: number = maxDistance + musicSystem.StaffLines[i].PositionAndShape.RelativePosition.y;
+                this.updateStaffLinesRelativePosition(musicSystem, i + 1, lowerStafflineYPos);
+            }
+        }
+        const firstStaffLine: StaffLine = musicSystem.StaffLines[0];
+        musicSystem.PositionAndShape.BorderTop = firstStaffLine.PositionAndShape.RelativePosition.y + firstStaffLine.PositionAndShape.BorderTop;
+        const lastStaffLine: StaffLine = musicSystem.StaffLines[musicSystem.StaffLines.length - 1];
+        musicSystem.PositionAndShape.BorderBottom = lastStaffLine.PositionAndShape.RelativePosition.y + lastStaffLine.PositionAndShape.BorderBottom;
+    }
+
+    /** Calculates the relative Positions of all MusicSystems.
+     *
+     */
+    protected calculateMusicSystemsRelativePositions(): void {
+        let currentPage: GraphicalMusicPage = this.createMusicPage();
+        let currentYPosition: number = 0;
+        // xPosition is always fixed
+        let currentSystem: MusicSystem = this.musicSystems[0];
+        let timesPageCouldntFitSingleSystem: number = 0;
+
+        for (let i: number = 0; i < this.musicSystems.length; i++) {
+            currentSystem = this.musicSystems[i];
+            if (currentPage.MusicSystems.length === 0) {
+                // if this is the first system on the current page:
+                // take top margins into account
+                this.addSystemToPage(currentPage, currentSystem);
+                if (this.rules.CompactMode) {
+                    currentYPosition = this.rules.PageTopMarginNarrow;
+                } else {
+                    currentYPosition = this.rules.PageTopMargin;
+                }
+
+                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:
+                // note: this is unnecessary. We have PageTopMargin and TitleBottomDistance for this.
+                //   also, this creates a sudden margin spike from PageTopMargin = 0.1 to PageTopMargin = 0.
+                // if (!this.rules.CompactMode) { // don't add extra margins/borders in compact mode
+                //     if (this.rules.PageTopMargin > 0) { // don't add extra margins with PageTopMargin == 0
+                //         currentYPosition += -currentSystem.PositionAndShape.BorderTop;
+                //     }
+                // }
+                const relativePosition: PointF2D = new PointF2D(this.rules.PageLeftMargin + this.rules.SystemLeftMargin,
+                                                                currentYPosition);
+                currentSystem.PositionAndShape.RelativePosition = relativePosition;
+                // check if the first system doesn't even fit on the page -> would lead to truncation at bottom end:
+                if (currentYPosition + currentSystem.PositionAndShape.BorderBottom > this.rules.PageHeight - this.rules.PageBottomMargin) {
+                    // can't fit single system on page, maybe PageFormat too small
+                    timesPageCouldntFitSingleSystem++;
+                    if (timesPageCouldntFitSingleSystem <= 4) { // only warn once with detailed info
+                        console.log(`warning: could not fit a single system on page ${currentPage.PageNumber}` +
+                            ` and measure number ${currentSystem.GraphicalMeasures[0][0].MeasureNumber}.
+                            The PageFormat may be too small for this sheet."
+                            Will not give further warnings for all pages, only total.`
+                        );
+                    }
+                }
+            } else {
+                // if this is not the first system on the page:
+                // find optimum distance between Systems
+                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.findRequiredDistanceWithSkyBottomLine(previousSystem, currentSystem);
+
+                // make sure the optical distance is the user-defined min distance:
+                distance += this.rules.MinSkyBottomDistBetweenSystems;
+
+                distance = Math.max(distance, this.rules.MinimumDistanceBetweenSystems + prevSystemLastStaffline.StaffHeight);
+                const newYPosition: number =    currentYPosition +
+                                                prevSystemLastStaffLineBB.RelativePosition.y +
+                                                distance;
+
+                // calculate the needed height for placing the current system on the page,
+                // to see if it still fits:
+                const currSystemBottomYPos: number =    newYPosition +
+                                                        currentSystem.PositionAndShape.BorderMarginBottom;
+                const doXmlPageBreak: boolean = this.rules.NewPageAtXMLNewPageAttribute && previousSystem.breaksPage;
+                if (!doXmlPageBreak &&
+                    (currSystemBottomYPos < this.rules.PageHeight - this.rules.PageBottomMargin)) {
+                    // enough space on this page:
+                    this.addSystemToPage(currentPage, currentSystem);
+                    currentYPosition = newYPosition;
+                    const relativePosition: PointF2D = new PointF2D(this.rules.PageLeftMargin + this.rules.SystemLeftMargin,
+                                                                    currentYPosition);
+                    currentSystem.PositionAndShape.RelativePosition = relativePosition;
+                } else {
+                    // new page needed:
+                    currentPage = this.createMusicPage();
+                    // re-check this system again:
+                    i -= 1;
+                    continue;
+                }
+            }
+        }
+        if (timesPageCouldntFitSingleSystem > 0) {
+            console.log(`total amount of pages that couldn't fit a single music system: ${timesPageCouldntFitSingleSystem} of ${currentPage.PageNumber}`);
+        }
+    }
+
+    /**
+     * Finds the minimum required distance between two systems
+     * with the help of the sky- and bottom lines
+     * @param upperSystem
+     * @param lowerSystem
+     */
+    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;
+        const lowerSkyLineArray: number[] = lowerSystemFirstStaffLine.SkyLine;
+        const upperStaffLineBB: BoundingBox = upperSystemLastStaffLine.PositionAndShape;
+        const lowerStaffLineBB: BoundingBox = lowerSystemFirstStaffLine.PositionAndShape;
+        const skylinePixelWidth: number = 1 / this.rules.SamplingUnit;
+        // Find maximum required space for sky and bottom line touching each other
+        let maxDistance: number = 0;
+        for (let upperIdx: number = 0; upperIdx < upperBottomLineArray.length; upperIdx++) {
+            const bottomLineValue: number = upperBottomLineArray[upperIdx];
+            // find index of the same x-position in lower skyline:
+            const lowerCenterIdx: number =  upperIdx +
+                                            Math.round((upperStaffLineBB.RelativePosition.x - lowerStaffLineBB.RelativePosition.x) * skylinePixelWidth);
+            if (lowerCenterIdx < 0) {
+                // should actually not happen..
+                continue;
+            }
+            if (lowerCenterIdx >= lowerSkyLineArray.length) {
+                // lower system ends earlier x-wise than upper system (e.g. at last system, if it is not stretched)
+                break;
+            }
+
+            // look at a range of +/- 2 Units to also ensure that objects are also not too close in x-direction:
+            const startIdx: number = Math.max(0, lowerCenterIdx - 6);
+            const endIdx: number = Math.min(lowerSkyLineArray.length - 1, lowerCenterIdx + 6);
+            let skylineValue: number = 0;
+            for (let lowerIdx: number = startIdx; lowerIdx <= endIdx; lowerIdx++) {
+                skylineValue = Math.min(skylineValue, lowerSkyLineArray[lowerIdx]);
+            }
+
+            const distance: number = bottomLineValue - skylineValue;
+            maxDistance = Math.max(distance, maxDistance);
+        }
+
+        if (maxDistance === 0) {
+            // can only happen when the bottom- and skyline have no x-overlap at all:
+            // fall back to borders:
+            maxDistance = upperStaffLineBB.BorderBottom - lowerStaffLineBB.BorderTop;
+        }
+
+        return maxDistance;
+    }
 }
+
 export class SystemBuildParameters {
     public currentSystem: MusicSystem;
     public systemMeasures: MeasureBuildParameters[] = [];

+ 96 - 38
src/MusicalScore/Graphical/SkyBottomLineCalculator.ts

@@ -1,13 +1,10 @@
-/* tslint:disable no-unused-variable */
-//FIXME: Enble tslint again when all functions are implemented and in use!
-
 import { EngravingRules } from "./EngravingRules";
 import { StaffLine } from "./StaffLine";
 import { PointF2D } from "../../Common/DataObjects/PointF2D";
 import { CanvasVexFlowBackend } from "./VexFlow/CanvasVexFlowBackend";
 import { VexFlowMeasure } from "./VexFlow/VexFlowMeasure";
 import { unitInPixels } from "./VexFlow/VexFlowMusicSheetDrawer";
-import * as log from "loglevel";
+import log from "loglevel";
 import { BoundingBox } from "./BoundingBox";
 /**
  * This class calculates and holds the skyline and bottom line information.
@@ -30,7 +27,7 @@ export class SkyBottomLineCalculator {
      */
     constructor(staffLineParent: StaffLine) {
         this.mStaffLineParent = staffLineParent;
-        this.mRules = EngravingRules.Rules;
+        this.mRules = staffLineParent.ParentMusicSystem.rules;
     }
 
     /**
@@ -43,7 +40,7 @@ export class SkyBottomLineCalculator {
         this.mBottomLine = [];
 
         // Create a temporary canvas outside the DOM to draw the measure in.
-        const tmpCanvas: any = new CanvasVexFlowBackend();
+        const tmpCanvas: any = new CanvasVexFlowBackend(this.StaffLineParent.ParentMusicSystem.rules);
         // search through all Measures
         for (const measure of this.StaffLineParent.Measures as VexFlowMeasure[]) {
             // must calculate first AbsolutePositions
@@ -51,11 +48,16 @@ export class SkyBottomLineCalculator {
 
             // Pre initialize and get stuff for more performance
             const vsStaff: any = measure.getVFStave();
+            let width: number = vsStaff.getWidth();
+            if (!(width > 0)) {
+                log.warn("SkyBottomLineCalculator: width not > 0 in measure " + measure.MeasureNumber);
+                width = 50;
+            }
             // Headless because we are outside the DOM
-            tmpCanvas.initializeHeadless(vsStaff.getWidth());
+            tmpCanvas.initializeHeadless(width);
             const ctx: any = tmpCanvas.getContext();
             const canvas: any = tmpCanvas.getCanvas();
-            const width: number = canvas.width;
+            width = canvas.width;
             const height: number = canvas.height;
 
             // This magic number is an offset from the top image border so that
@@ -64,11 +66,16 @@ 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);
-            measure.draw(ctx);
+            try {
+                measure.draw(ctx);
+                // Vexflow errors can happen here, then our complete rendering loop would halt without catching errors.
+            } catch (ex) {
+                log.warn("SkyBottomLineCalculator.calculateLines.draw", ex);
+            }
 
             // imageData.data is a Uint8ClampedArray representing a one-dimensional array containing the data in the RGBA order
             // RGBA is 32 bit word with 8 bits red, 8 bits green, 8 bits blue and 8 bit alpha. Alpha should be 0 for all background colors.
@@ -100,6 +107,13 @@ export class SkyBottomLineCalculator {
                     }
                 }
             }
+
+            for (let idx: number = 0; idx < tmpSkyLine.length; idx++) {
+                if (tmpSkyLine[idx] === undefined) {
+                    tmpSkyLine[idx] = Math.max(this.findPreviousValidNumber(idx, tmpSkyLine), this.findNextValidNumber(idx, tmpSkyLine));
+                }
+            }
+
             this.mSkyLine.push(...tmpSkyLine);
             this.mBottomLine.push(...tmpBottomLine);
 
@@ -114,7 +128,7 @@ export class SkyBottomLineCalculator {
             tmpCanvas.clear();
         }
         // Subsampling:
-        // The pixel width is bigger then the measure size in units. So we split the array into
+        // The pixel width is bigger than the measure size in units. So we split the array into
         // chunks with the size of MeasurePixelWidth/measureUnitWidth and reduce the value to its
         // average
         const arrayChunkSize: number = this.mSkyLine.length / arrayLength;
@@ -122,23 +136,67 @@ export class SkyBottomLineCalculator {
         const subSampledSkyLine: number[] = [];
         const subSampledBottomLine: number[] = [];
         for (let chunkIndex: number = 0; chunkIndex < this.mSkyLine.length; chunkIndex += arrayChunkSize) {
-            let chunk: number[] = this.mSkyLine.slice(chunkIndex, chunkIndex + arrayChunkSize);
+            if (subSampledSkyLine.length === arrayLength) {
+                break; // TODO find out why skyline.length becomes arrayLength + 1. see log.debug below
+            }
+
+            const endIndex: number = Math.min(this.mSkyLine.length, chunkIndex + arrayChunkSize);
+            let chunk: number[] = this.mSkyLine.slice(chunkIndex, endIndex + 1); // slice does not include end index
+            // TODO chunkIndex + arrayChunkSize is sometimes bigger than this.mSkyLine.length -> out of bounds
+            // TODO chunkIndex + arrayChunkSize is often a non-rounded float as well. is that ok to use with slice?
+            /*const diff: number = this.mSkyLine.length - (chunkIndex + arrayChunkSize);
+            if (diff < 0) { // out of bounds
+                console.log("length - slice end index: " + diff);
+            }*/
+
             subSampledSkyLine.push(Math.min(...chunk));
-            chunk = this.mBottomLine.slice(chunkIndex, chunkIndex + arrayChunkSize);
+            chunk = this.mBottomLine.slice(chunkIndex, endIndex + 1); // slice does not include end index
             subSampledBottomLine.push(Math.max(...chunk));
         }
 
         this.mSkyLine = subSampledSkyLine;
         this.mBottomLine = subSampledBottomLine;
-        if (this.mSkyLine.length !== arrayLength) {
+        if (this.mSkyLine.length !== arrayLength) { // bottomline will always be same length as well
             log.debug(`SkyLine calculation was not correct (${this.mSkyLine.length} instead of ${arrayLength})`);
         }
-        if (this.mBottomLine.length !== arrayLength) {
-            log.debug(`BottomLine calculation was not correct (${this.mBottomLine.length} instead of ${arrayLength})`);
-        }
         // Remap the values from 0 to +/- height in units
-        this.mSkyLine = this.mSkyLine.map(v => (v - Math.max(...this.mSkyLine)) / unitInPixels);
-        this.mBottomLine = this.mBottomLine.map(v => (v - Math.min(...this.mBottomLine)) / unitInPixels + this.mRules.StaffHeight);
+        this.mSkyLine = this.mSkyLine.map(v => (v - Math.max(...this.mSkyLine)) / unitInPixels + this.StaffLineParent.TopLineOffset);
+        this.mBottomLine = this.mBottomLine.map(v => (v - Math.min(...this.mBottomLine)) / unitInPixels + this.StaffLineParent.BottomLineOffset);
+    }
+
+    /**
+     * go backwards through the skyline array and find a number so that
+     * we can properly calculate the average
+     * @param start
+     * @param backend
+     * @param color
+     */
+    private findPreviousValidNumber(start: number, tSkyLine: number[]): number {
+        for (let idx: number = start; idx >= 0; idx--) {
+            if (!isNaN(tSkyLine[idx])) {
+                return tSkyLine[idx];
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * go forward through the skyline array and find a number so that
+     * we can properly calculate the average
+     * @param start
+     * @param backend
+     * @param color
+     */
+    private findNextValidNumber(start: number, tSkyLine: Array<number>): number {
+        if (start >= tSkyLine.length) {
+            return tSkyLine[start - 1];
+        }
+        for (let idx: number = start; idx < tSkyLine.length; idx++) {
+            if (!isNaN(tSkyLine[idx])) {
+                return tSkyLine[idx];
+            }
+        }
+        return 0;
     }
 
     /**
@@ -157,8 +215,8 @@ export class SkyBottomLineCalculator {
 
     /**
      * This method updates the SkyLine for a given Wedge.
-     * @param start Start point of the wedge
-     * @param end End point of the wedge
+     * @param start Start point of the wedge (the point where both lines meet)
+     * @param end End point of the wedge (the end of the most extreme line: upper line for skyline, lower line for bottomline)
      */
     public updateSkyLineWithWedge(start: PointF2D, end: PointF2D): void {
         // FIXME: Refactor if wedges will be added. Current status is that vexflow will be used for this
@@ -241,7 +299,7 @@ export class SkyBottomLineCalculator {
      * This method updates the BottomLine for a given range with a given value
      * @param  to update the BottomLine for
      * @param start Start index of the range
-     * @param end End index of the range
+     * @param end End index of the range (excluding)
      * @param value ??
      */
     public updateBottomLineInRange(startIndex: number, endIndex: number, value: number): void {
@@ -252,7 +310,7 @@ export class SkyBottomLineCalculator {
      * Resets a SkyLine in a range to its original value
      * @param  to reset the SkyLine in
      * @param startIndex Start index of the range
-     * @param endIndex End index of the range
+     * @param endIndex End index of the range (excluding)
      */
     public resetSkyLineInRange(startIndex: number, endIndex: number): void {
         this.updateInRange(this.SkyLine, startIndex, endIndex);
@@ -339,7 +397,7 @@ export class SkyBottomLineCalculator {
      * This method finds the SkyLine's minimum value within a given range.
      * @param staffLine Staffline to apply to
      * @param startIndex Starting index
-     * @param endIndex End index
+     * @param endIndex End index (including)
      */
     public getSkyLineMinInRange(startIndex: number, endIndex: number): number {
         return this.getMinInRange(this.SkyLine, startIndex, endIndex);
@@ -362,7 +420,7 @@ export class SkyBottomLineCalculator {
      * This method finds the BottomLine's maximum value within a given range.
      * @param staffLine Staffline to find the max value in
      * @param startIndex Start index of the range
-     * @param endIndex End index of the range
+     * @param endIndex End index of the range (excluding)
      */
     public getBottomLineMaxInRange(startIndex: number, endIndex: number): number {
         return this.getMaxInRange(this.BottomLine, startIndex, endIndex);
@@ -384,13 +442,13 @@ export class SkyBottomLineCalculator {
     //#region Private methods
 
     /**
-     * Updates sky- and bottom line with a boundingBox and it's children
+     * Updates sky- and bottom line with a boundingBox and its children
      * @param boundingBox Bounding box to be added
      * @param topBorder top
      */
-    public updateWithBoundingBoxRecursivly(boundingBox: BoundingBox): void {
+    public updateWithBoundingBoxRecursively(boundingBox: BoundingBox): void {
         if (boundingBox.ChildElements && boundingBox.ChildElements.length > 0) {
-            this.updateWithBoundingBoxRecursivly(boundingBox);
+            this.updateWithBoundingBoxRecursively(boundingBox);
         } else {
             const currentTopBorder: number = boundingBox.BorderTop + boundingBox.AbsolutePosition.y;
             const currentBottomBorder: number = boundingBox.BorderBottom + boundingBox.AbsolutePosition.y;
@@ -400,7 +458,7 @@ export class SkyBottomLineCalculator {
                 const endPoint: number = Math.ceil(boundingBox.AbsolutePosition.x + boundingBox.BorderRight) ;
 
                 this.updateInRange(this.mSkyLine, startPoint, endPoint, currentTopBorder);
-            } else if (currentBottomBorder > this.mRules.StaffHeight) {
+            } else if (currentBottomBorder > this.StaffLineParent.StaffHeight) {
                 const startPoint: number = Math.floor(boundingBox.AbsolutePosition.x + boundingBox.BorderLeft);
                 const endPoint: number = Math.ceil(boundingBox.AbsolutePosition.x + boundingBox.BorderRight);
 
@@ -413,7 +471,7 @@ export class SkyBottomLineCalculator {
      * Update an array with the value given inside a range. NOTE: will only be updated if value > oldValue
      * @param array Array to fill in the new value
      * @param startIndex start index to begin with (default: 0)
-     * @param endIndex end index of array (default: array length)
+     * @param endIndex end index of array (excluding, default: array length)
      * @param value value to fill in (default: 0)
      */
     private updateInRange(array: number[], startIndex: number = 0, endIndex: number = array.length, value: number = 0): void {
@@ -421,7 +479,7 @@ export class SkyBottomLineCalculator {
         endIndex = Math.ceil(endIndex * this.SamplingUnit);
 
         if (endIndex < startIndex) {
-            throw new Error("start index of line is greater then the end index");
+            throw new Error("start index of line is greater than the end index");
         }
 
         if (startIndex < 0) {
@@ -441,7 +499,7 @@ export class SkyBottomLineCalculator {
      * Sets the value given to the range inside the array. NOTE: will always update the value
      * @param array Array to fill in the new value
      * @param startIndex start index to begin with (default: 0)
-     * @param endIndex end index of array (default: array length)
+     * @param endIndex end index of array (excluding, default: array length)
      * @param value value to fill in (default: 0)
      */
     private setInRange(array: number[], startIndex: number = 0, endIndex: number = array.length, value: number = 0): void {
@@ -468,13 +526,13 @@ export class SkyBottomLineCalculator {
      * Get all values of the selected line inside the given range
      * @param skyBottomArray Skyline or bottom line
      * @param startIndex start index
-     * @param endIndex end index
+     * @param endIndex end index (including)
      */
     private getMinInRange(skyBottomArray: number[], startIndex: number, endIndex: number): number {
         startIndex = Math.floor(startIndex * this.SamplingUnit);
         endIndex = Math.ceil(endIndex * this.SamplingUnit);
 
-        if (skyBottomArray === undefined) {
+        if (!skyBottomArray) {
             // Highly questionable
             return Number.MAX_VALUE;
         }
@@ -493,7 +551,7 @@ export class SkyBottomLineCalculator {
         }
 
         if (startIndex >= 0 && endIndex <= skyBottomArray.length) {
-            return Math.min(...skyBottomArray.slice(startIndex, endIndex));
+            return Math.min(...skyBottomArray.slice(startIndex, endIndex + 1)); // slice does not include end (index)
         }
     }
 
@@ -501,13 +559,13 @@ export class SkyBottomLineCalculator {
      * Get the maximum value inside the given indices
      * @param skyBottomArray Skyline or bottom line
      * @param startIndex start index
-     * @param endIndex end index
+     * @param endIndex end index (including)
      */
     private getMaxInRange(skyBottomArray: number[], startIndex: number, endIndex: number): number {
         startIndex = Math.floor(startIndex * this.SamplingUnit);
         endIndex = Math.ceil(endIndex * this.SamplingUnit);
 
-        if (skyBottomArray === undefined) {
+        if (!skyBottomArray) {
             // Highly questionable
             return Number.MIN_VALUE;
         }
@@ -526,7 +584,7 @@ export class SkyBottomLineCalculator {
         }
 
         if (startIndex >= 0 && endIndex <= skyBottomArray.length) {
-            return Math.max(...skyBottomArray.slice(startIndex, endIndex));
+            return Math.max(...skyBottomArray.slice(startIndex, endIndex + 1)); // slice does not include end (index)
         }
     }
     // FIXME: What does this do here?

+ 59 - 7
src/MusicalScore/Graphical/StaffLine.ts

@@ -12,7 +12,6 @@ import {GraphicalLabel} from "./GraphicalLabel";
 import { SkyBottomLineCalculator } from "./SkyBottomLineCalculator";
 import { GraphicalOctaveShift } from "./GraphicalOctaveShift";
 import { GraphicalSlur } from "./GraphicalSlur";
-import { AlignmentManager } from "./AlignmentManager";
 import { AbstractGraphicalExpression } from "./AbstractGraphicalExpression";
 
 /**
@@ -26,10 +25,13 @@ export abstract class StaffLine extends GraphicalObject {
     protected parentStaff: Staff;
     protected octaveShifts: GraphicalOctaveShift[] = [];
     protected skyBottomLine: SkyBottomLineCalculator;
-    protected alignmentManager: AlignmentManager;
     protected lyricLines: GraphicalLine[] = [];
     protected lyricsDashes: GraphicalLabel[] = [];
     protected abstractExpressions: AbstractGraphicalExpression[] = [];
+    /** The staff height in units */
+    private staffHeight: number;
+    private topLineOffset: number;
+    private bottomLineOffset: number;
 
     // For displaying Slurs
     protected graphicalSlurs: GraphicalSlur[] = [];
@@ -40,7 +42,50 @@ export abstract class StaffLine extends GraphicalObject {
         this.parentStaff = parentStaff;
         this.boundingBox = new BoundingBox(this, parentSystem.PositionAndShape);
         this.skyBottomLine = new SkyBottomLineCalculator(this);
-        this.alignmentManager = new AlignmentManager(this);
+        this.staffHeight = this.parentMusicSystem.rules.StaffHeight;
+        this.topLineOffset = 0;
+        this.bottomLineOffset = 4;
+
+        this.calculateStaffLineOffsets();
+    }
+
+    /**
+     * If the musicXML sets different numbers of stafflines, we need to have different offsets
+     * to accomodate this - primarily for the sky and bottom lines and cursor.
+     */
+    private calculateStaffLineOffsets(): void {
+        if (this.ParentStaff.isTab) {
+            switch (this.ParentStaff.StafflineCount) {
+                case 5:
+                    this.staffHeight = this.bottomLineOffset =
+                        this.ParentStaff.ParentInstrument.GetMusicSheet.Rules.TabStaffInterlineHeight * 6;
+                    break;
+                default:
+                    this.staffHeight = this.bottomLineOffset =
+                        this.ParentStaff.ParentInstrument.GetMusicSheet.Rules.TabStaffInterlineHeight * this.ParentStaff.StafflineCount;
+                    break;
+            }
+        } else {
+            switch (this.ParentStaff.StafflineCount) {
+                case 4:
+                    this.bottomLineOffset = 1;
+                    break;
+                case 3:
+                    this.topLineOffset = 1;
+                    this.bottomLineOffset = 1;
+                    break;
+                case 2:
+                    this.topLineOffset = 2;
+                    this.bottomLineOffset = 1;
+                    break;
+                case 1:
+                    this.topLineOffset = 2;
+                    this.bottomLineOffset = 2;
+                    break;
+                default:
+                    break;
+            }
+        }
     }
 
     public get Measures(): GraphicalMeasure[] {
@@ -104,10 +149,6 @@ export abstract class StaffLine extends GraphicalObject {
         this.parentStaff = value;
     }
 
-    public get AlignmentManager(): AlignmentManager {
-        return this.alignmentManager;
-    }
-
     public get SkyBottomLineCalculator(): SkyBottomLineCalculator {
         return this.skyBottomLine;
     }
@@ -128,6 +169,17 @@ export abstract class StaffLine extends GraphicalObject {
         this.octaveShifts = value;
     }
 
+    public get StaffHeight(): number {
+        return this.staffHeight;
+    }
+
+    public get TopLineOffset(): number {
+        return this.topLineOffset;
+    }
+    public get BottomLineOffset(): number {
+        return this.bottomLineOffset;
+    }
+
     // get all Graphical Slurs of a staffline
     public get GraphicalSlurs(): GraphicalSlur[] {
         return this.graphicalSlurs;

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

@@ -17,7 +17,7 @@ export class SystemLine extends GraphicalObject {
         this.parentMusicSystem = musicSystem;
         this.topMeasure = topMeasure;
         this.bottomMeasure = bottomMeasure;
-        this.parentTopStaffLine = topMeasure.ParentStaffLine;
+        this.parentTopStaffLine = topMeasure?.ParentStaffLine;
         this.boundingBox = new BoundingBox(this, musicSystem.PositionAndShape);
     }
 

+ 43 - 7
src/MusicalScore/Graphical/SystemLinesEnum.ts

@@ -1,9 +1,45 @@
 export enum SystemLinesEnum {
-    SingleThin = 0,
-    DoubleThin = 1,
-    ThinBold = 2,
-    BoldThinDots = 3,
-    DotsThinBold = 4,
-    DotsBoldBoldDots = 5,
-    None = 6
+    SingleThin = 0,       /*SINGLE,       [bar-style=regular]*/
+    DoubleThin = 1,       /*DOUBLE,       [bar-style=light-light]*/
+    ThinBold = 2,         /*END,          [bar-style=light-heavy]*/
+    BoldThinDots = 3,     /*REPEAT_BEGIN, repeat[direction=forward]*/
+    DotsThinBold = 4,     /*REPEAT_END,   repeat[direction=backward]*/
+    DotsBoldBoldDots = 5, /*REPEAT_BOTH*/
+    None = 6,             /*              [bar-style=none]*/
+    Dotted = 7,           /*              [bar-style=dotted]*/
+    Dashed = 8,           /*              [bar-style=dashed]*/
+    Bold = 9,             /*              [bar-style=heavy]*/
+    BoldThin = 10,        /*              [bar-style=heavy-light]*/
+    DoubleBold = 11,      /*              [bar-style=heavy-heavy]*/
+    Tick = 12,            /*              [bar-style=tick]*/
+    Short = 13            /*              [bar-style=short]*/
+}
+
+export class SystemLinesEnumHelper {
+    public static xmlBarlineStyleToSystemLinesEnum(xmlValue: string): SystemLinesEnum {
+        if (xmlValue === "regular") {
+            return SystemLinesEnum.SingleThin;
+        } else if (xmlValue === "dotted") {
+            return SystemLinesEnum.Dotted;
+        } else if (xmlValue === "dashed") {
+            return SystemLinesEnum.Dashed;
+        } else if (xmlValue === "heavy") {
+            return SystemLinesEnum.Bold;
+        } else if (xmlValue === "light-light") {
+            return SystemLinesEnum.DoubleThin;
+        } else if (xmlValue === "light-heavy") {
+            return SystemLinesEnum.ThinBold;
+        } else if (xmlValue === "heavy-light") {
+            return SystemLinesEnum.BoldThin;
+        } else if (xmlValue === "heavy-heavy") {
+            return SystemLinesEnum.DoubleBold;
+        } else if (xmlValue === "tick") {
+            return SystemLinesEnum.Tick;
+        } else if (xmlValue === "short") {
+            return SystemLinesEnum.Short;
+        } else if (xmlValue === "none") {
+            return SystemLinesEnum.None;
+        }
+        return SystemLinesEnum.SingleThin;
+    }
 }

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

@@ -57,7 +57,7 @@ export class VerticalGraphicalStaffEntryContainer {
     public getFirstNonNullStaffEntry(): GraphicalStaffEntry {
         for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
             const graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx];
-            if (graphicalStaffEntry !== undefined) {
+            if (graphicalStaffEntry) {
                 return graphicalStaffEntry;
             }
         }

+ 44 - 12
src/MusicalScore/Graphical/AlignmentManager.ts → src/MusicalScore/Graphical/VexFlow/AlignmentManager.ts

@@ -1,16 +1,18 @@
-import { StaffLine } from "./StaffLine";
-import { BoundingBox } from "./BoundingBox";
-import { VexFlowContinuousDynamicExpression } from "./VexFlow/VexFlowContinuousDynamicExpression";
-import { AbstractGraphicalExpression } from "./AbstractGraphicalExpression";
-import { PointF2D } from "../../Common/DataObjects/PointF2D";
-import { EngravingRules } from "./EngravingRules";
+import { StaffLine } from "../StaffLine";
+import { BoundingBox } from "../BoundingBox";
+import { VexFlowContinuousDynamicExpression } from "./VexFlowContinuousDynamicExpression";
+import { AbstractGraphicalExpression } from "../AbstractGraphicalExpression";
+import { PointF2D } from "../../../Common/DataObjects/PointF2D";
+import { EngravingRules } from "../EngravingRules";
+import { PlacementEnum } from "../../VoiceData/Expressions/AbstractExpression";
 
 export class AlignmentManager {
     private parentStaffline: StaffLine;
-    private rules: EngravingRules = EngravingRules.Rules;
+    private rules: EngravingRules;
 
     constructor(staffline: StaffLine) {
         this.parentStaffline = staffline;
+        this.rules = this.parentStaffline.ParentMusicSystem.rules;
     }
 
     public alignDynamicExpressions(): void {
@@ -20,9 +22,25 @@ export class AlignmentManager {
         for (let aeIdx: number = 0; aeIdx < this.parentStaffline.AbstractExpressions.length - 1; aeIdx++) {
             const currentExpression: AbstractGraphicalExpression = this.parentStaffline.AbstractExpressions[aeIdx];
             const nextExpression: AbstractGraphicalExpression = this.parentStaffline.AbstractExpressions[aeIdx + 1];
-            if (currentExpression.Placement === nextExpression.Placement) {
+
+            const currentExpressionPlacement: PlacementEnum = currentExpression?.SourceExpression?.Placement;
+            const nextExpressionPlacement: PlacementEnum = nextExpression?.SourceExpression?.Placement;
+
+            // TODO this shifts dynamics in An die Ferne Geliebte, showing that there's something wrong with the RelativePositions etc with wedges
+            // if (currentExpression instanceof GraphicalContinuousDynamicExpression) {
+            //     currentExpression.calcPsi();
+            // }
+            // if (nextExpression instanceof GraphicalContinuousDynamicExpression) {
+            //     nextExpression.calcPsi();
+            // }
+
+            if (currentExpressionPlacement === nextExpressionPlacement) {
+                // if ((currentExpression as any).label?.label?.text?.startsWith("dim") ||
+                //     (nextExpression as any).label?.label?.text?.startsWith("dim")) {
+                //         console.log("here");
+                //     }
                 const dist: PointF2D = this.getDistance(currentExpression.PositionAndShape, nextExpression.PositionAndShape);
-                if (dist.x < this.rules.DynamicExpressionMaxDistance) {
+                if (Math.abs(dist.x) < this.rules.DynamicExpressionMaxDistance) {
                     // Prevent last found expression to be added twice. e.g. p<f as three close expressions
                     if (tmpList.indexOf(currentExpression) === -1) {
                         tmpList.push(currentExpression);
@@ -41,18 +59,31 @@ export class AlignmentManager {
             if (aes.length > 0) {
                 // Get the median y position and shift all group members to that position
                 const centerYs: number[] = aes.map(expr => expr.PositionAndShape.Center.y);
+                // TODO this may not give the right position for wedges (GraphicalContinuousDynamic, !isVerbal())
                 const yIdeal: number = Math.max(...centerYs);
+                // for (const ae of aes) { // debug
+                //     if (ae.PositionAndShape.Center.y > 6) {
+                //         // dynamic positioned at edge of skybottomline
+                //         console.log(`max expression in measure ${ae.SourceExpression.parentMeasure.MeasureNumber}: `);
+                //         console.dir(aes);
+                //     }
+                // }
+
                 for (let exprIdx: number = 0; exprIdx < aes.length; exprIdx++) {
                     const expr: AbstractGraphicalExpression = aes[exprIdx];
                     const centerOffset: number = centerYs[exprIdx] - yIdeal;
+                    // TODO centerOffset is way too big sometimes, like 7.0 in An die Ferne Geliebte (measure 10, dim.)
                     // FIXME: Expressions should not behave differently.
                     if (expr instanceof VexFlowContinuousDynamicExpression) {
                         (expr as VexFlowContinuousDynamicExpression).shiftYPosition(-centerOffset);
+                        (expr as VexFlowContinuousDynamicExpression).calcPsi();
                     } else {
-                        // TODO: The 0.8 are because the letters are a bit to far done
+                        // TODO: The 0.8 are because the letters are a bit too far done
                         expr.PositionAndShape.RelativePosition.y -= centerOffset * 0.8;
+                        // note: verbal GraphicalContinuousDynamicExpressions have a label, nonverbal ones don't.
+                        // take care to update and take the right bounding box for skyline.
+                        expr.PositionAndShape.calculateBoundingBox();
                     }
-                    expr.PositionAndShape.calculateBoundingBox();
                     // Squeeze wedges
                     if ((expr as VexFlowContinuousDynamicExpression).squeeze) {
                         const nextExpression: AbstractGraphicalExpression = exprIdx < aes.length - 1 ? aes[exprIdx + 1] : undefined;
@@ -79,10 +110,11 @@ export class AlignmentManager {
     private getDistance(a: BoundingBox, b: BoundingBox): PointF2D {
         const rightBorderA: number = a.RelativePosition.x + a.BorderMarginRight;
         const leftBorderB: number = b.RelativePosition.x + b.BorderMarginLeft;
-        const bottomBorderA: number = b.RelativePosition.y + a.BorderMarginBottom;
+        const bottomBorderA: number = a.RelativePosition.y + a.BorderMarginBottom;
         const topBorderB: number = b.RelativePosition.y + b.BorderMarginTop;
         return new PointF2D(leftBorderB - rightBorderA,
                             topBorderB - bottomBorderA);
+                            // note: this is a distance vector, not absolute distance, otherwise we need Math.abs
     }
 
     /**

+ 80 - 31
src/MusicalScore/Graphical/VexFlow/CanvasVexFlowBackend.ts

@@ -1,4 +1,4 @@
-import Vex = require("vexflow");
+import Vex from "vexflow";
 
 import {VexFlowBackend} from "./VexFlowBackend";
 import {FontStyles} from "../../../Common/Enums/FontStyles";
@@ -6,23 +6,49 @@ import {Fonts} from "../../../Common/Enums/Fonts";
 import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
 import {PointF2D} from "../../../Common/DataObjects/PointF2D";
 import {VexFlowConverter} from "./VexFlowConverter";
+import {BackendType} from "../../../OpenSheetMusicDisplay/OSMDOptions";
+import {EngravingRules} from "../EngravingRules";
+import {GraphicalMusicPage} from "../GraphicalMusicPage";
 
 export class CanvasVexFlowBackend extends VexFlowBackend {
+    private zoom: number;
 
-    public getBackendType(): number {
+    constructor(rules: EngravingRules) {
+        super();
+        this.rules = rules;
+    }
+
+    public getVexflowBackendType(): Vex.Flow.Renderer.Backends {
         return Vex.Flow.Renderer.Backends.CANVAS;
     }
 
-    public initialize(container: HTMLElement): void {
+    public getOSMDBackendType(): BackendType {
+        return BackendType.Canvas;
+    }
+
+    public getCanvasSize(): number {
+        return document.getElementById("osmdCanvasPage" + this.graphicalMusicPage.PageNumber)?.offsetHeight;
+        // smaller inner canvas:
+        // return Number.parseInt(
+        //     document.getElementById("osmdCanvasVexFlowBackendCanvas" + this.graphicalMusicPage.PageNumber)?.style.height, 10);
+    }
+
+    public initialize(container: HTMLElement, zoom: number): void {
+        this.zoom = zoom;
         this.canvas = document.createElement("canvas");
+        if (!this.graphicalMusicPage) {
+            this.graphicalMusicPage = new GraphicalMusicPage(undefined);
+            this.graphicalMusicPage.PageNumber = 1;
+        }
+        this.canvas.id = "osmdCanvasVexFlowBackendCanvas" + this.graphicalMusicPage.PageNumber; // needed to extract image buffer from js
         this.inner = document.createElement("div");
+        this.inner.id = "osmdCanvasPage" + this.graphicalMusicPage.PageNumber;
         this.inner.style.position = "relative";
         this.canvas.style.zIndex = "0";
         this.inner.appendChild(this.canvas);
         container.appendChild(this.inner);
-        this.renderer = new Vex.Flow.Renderer(this.canvas, this.getBackendType());
+        this.renderer = new Vex.Flow.Renderer(this.canvas, this.getVexflowBackendType());
         this.ctx = <Vex.Flow.CanvasContext>this.renderer.getContext();
-        this.canvasRenderingCtx = this.ctx.vexFlowCanvasContext;
     }
 
     /**
@@ -31,12 +57,16 @@ export class CanvasVexFlowBackend extends VexFlowBackend {
      * @param height Height of the canvas
      */
     public initializeHeadless(width: number = 300, height: number = 300): void {
+        if (!this.graphicalMusicPage) {
+            // not needed here yet, but just for future safety, make sure the page isn't undefined
+            this.graphicalMusicPage = new GraphicalMusicPage(undefined);
+            this.graphicalMusicPage.PageNumber = 1;
+        }
         this.canvas = document.createElement("canvas");
         (this.canvas as any).width = width;
         (this.canvas as any).height = height;
-        this.renderer = new Vex.Flow.Renderer(this.canvas, this.getBackendType());
+        this.renderer = new Vex.Flow.Renderer(this.canvas, this.getVexflowBackendType());
         this.ctx = <Vex.Flow.CanvasContext>this.renderer.getContext();
-        this.canvasRenderingCtx = this.ctx.vexFlowCanvasContext;
     }
 
     public getContext(): Vex.Flow.CanvasContext {
@@ -45,6 +75,16 @@ export class CanvasVexFlowBackend extends VexFlowBackend {
 
     public clear(): void {
         (<any>this.ctx).clearRect(0, 0, (<any>this.canvas).width, (<any>this.canvas).height);
+
+        // set background color if not transparent
+        if (this.rules.PageBackgroundColor) {
+            this.ctx.save();
+            // note that this will hide the cursor
+            this.ctx.setFillStyle(this.rules.PageBackgroundColor);
+            this.zoom = 1; // remove
+            this.ctx.fillRect(0, 0, (this.canvas as any).width / this.zoom, (this.canvas as any).height / this.zoom);
+            this.ctx.restore();
+        }
     }
 
     public scale(k: number): void {
@@ -52,40 +92,43 @@ export class CanvasVexFlowBackend extends VexFlowBackend {
     }
 
     public translate(x: number, y: number): void {
-        this.canvasRenderingCtx.translate(x, y);
+        this.CanvasRenderingCtx.translate(x, y);
     }
     public renderText(fontHeight: number, fontStyle: FontStyles, font: Fonts, text: string,
-                      heightInPixel: number, screenPosition: PointF2D, color: string = undefined): void  {
-        const old: string = this.canvasRenderingCtx.font;
-        this.canvasRenderingCtx.save();
-        this.canvasRenderingCtx.font = VexFlowConverter.font(
+                      heightInPixel: number, screenPosition: PointF2D,
+                      color: string = undefined, fontFamily: string = undefined): void  {
+        const old: string = this.CanvasRenderingCtx.font;
+        this.CanvasRenderingCtx.save();
+        this.CanvasRenderingCtx.font = VexFlowConverter.font(
             fontHeight,
             fontStyle,
-            font
+            font,
+            this.rules,
+            fontFamily
         );
-        this.canvasRenderingCtx.fillStyle = color;
-        this.canvasRenderingCtx.strokeStyle = color;
-        this.canvasRenderingCtx.fillText(text, screenPosition.x, screenPosition.y + heightInPixel);
-        this.canvasRenderingCtx.restore();
-        this.canvasRenderingCtx.font = old;
+        this.CanvasRenderingCtx.fillStyle = color;
+        this.CanvasRenderingCtx.strokeStyle = color;
+        this.CanvasRenderingCtx.fillText(text, screenPosition.x, screenPosition.y + heightInPixel);
+        this.CanvasRenderingCtx.restore();
+        this.CanvasRenderingCtx.font = old;
     }
     public renderRectangle(rectangle: RectangleF2D, styleId: number, alpha: number = 1): void {
-        const old: string | CanvasGradient | CanvasPattern = this.canvasRenderingCtx.fillStyle;
-        this.canvasRenderingCtx.fillStyle = VexFlowConverter.style(styleId);
-        this.canvasRenderingCtx.globalAlpha = alpha;
+        const old: string | CanvasGradient | CanvasPattern = this.CanvasRenderingCtx.fillStyle;
+        this.CanvasRenderingCtx.fillStyle = VexFlowConverter.style(styleId);
+        this.CanvasRenderingCtx.globalAlpha = alpha;
         this.ctx.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
-        this.canvasRenderingCtx.fillStyle = old;
-        this.canvasRenderingCtx.globalAlpha = 1;
+        this.CanvasRenderingCtx.fillStyle = old;
+        this.CanvasRenderingCtx.globalAlpha = 1;
     }
 
     public renderLine(start: PointF2D, stop: PointF2D, color: string = "#FF0000FF", lineWidth: number= 2): void {
-        const oldStyle: string | CanvasGradient | CanvasPattern = this.canvasRenderingCtx.strokeStyle;
-        this.canvasRenderingCtx.strokeStyle = color;
-        this.canvasRenderingCtx.beginPath();
-        this.canvasRenderingCtx.moveTo(start.x, start.y);
-        this.canvasRenderingCtx.lineTo(stop.x, stop.y);
-        this.canvasRenderingCtx.stroke();
-        this.canvasRenderingCtx.strokeStyle = oldStyle;
+        const oldStyle: string | CanvasGradient | CanvasPattern = this.CanvasRenderingCtx.strokeStyle;
+        this.CanvasRenderingCtx.strokeStyle = color;
+        this.CanvasRenderingCtx.beginPath();
+        this.CanvasRenderingCtx.moveTo(start.x, start.y);
+        this.CanvasRenderingCtx.lineTo(stop.x, stop.y);
+        this.CanvasRenderingCtx.stroke();
+        this.CanvasRenderingCtx.strokeStyle = oldStyle;
     }
 
     public renderCurve(points: PointF2D[]): void {
@@ -115,5 +158,11 @@ export class CanvasVexFlowBackend extends VexFlowBackend {
     }
 
     private ctx: Vex.Flow.CanvasContext;
-    private canvasRenderingCtx: CanvasRenderingContext2D;
+
+    public get CanvasRenderingCtx(): CanvasRenderingContext2D {
+        // This clusterfuck is only there to counter act my favorite vexflow line:
+        // ctx.vexFlowCanvasContext = ctx;
+        // No idea why they are saving the context but we wrap the types here
+        return <CanvasRenderingContext2D>(this.ctx as any).vexFlowCanvasContext;
+    }
 }

+ 68 - 8
src/MusicalScore/Graphical/VexFlow/SvgVexFlowBackend.ts

@@ -1,4 +1,4 @@
-import Vex = require("vexflow");
+import Vex from "vexflow";
 
 import {VexFlowBackend} from "./VexFlowBackend";
 import {VexFlowConverter} from "./VexFlowConverter";
@@ -6,40 +6,75 @@ import {FontStyles} from "../../../Common/Enums/FontStyles";
 import {Fonts} from "../../../Common/Enums/Fonts";
 import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
 import {PointF2D} from "../../../Common/DataObjects/PointF2D";
+import {BackendType} from "../../../OpenSheetMusicDisplay/OSMDOptions";
+import {EngravingRules} from "../EngravingRules";
 
 export class SvgVexFlowBackend extends VexFlowBackend {
 
     private ctx: Vex.Flow.SVGContext;
+    private zoom: number;
 
-    public getBackendType(): number {
+    constructor(rules: EngravingRules) {
+        super();
+        this.rules = rules;
+    }
+
+    public getVexflowBackendType(): Vex.Flow.Renderer.Backends {
         return Vex.Flow.Renderer.Backends.SVG;
     }
 
-    public initialize(container: HTMLElement): void {
+    public getOSMDBackendType(): BackendType {
+        return BackendType.SVG;
+    }
+
+    public getCanvasSize(): number {
+        return document.getElementById("osmdCanvasPage" + this.graphicalMusicPage.PageNumber)?.offsetHeight;
+    }
+
+    public initialize(container: HTMLElement, zoom: number): void {
+        this.zoom = zoom;
         this.canvas = document.createElement("div");
+        this.canvas.id = "osmdCanvasPage" + this.graphicalMusicPage.PageNumber;
+        // this.canvas.id = uniqueID // TODO create unique tagName like with cursor now?
         this.inner = this.canvas;
         this.inner.style.position = "relative";
         this.canvas.style.zIndex = "0";
         container.appendChild(this.inner);
-        this.renderer = new Vex.Flow.Renderer(this.canvas, this.getBackendType());
+        this.renderer = new Vex.Flow.Renderer(this.canvas, this.getVexflowBackendType());
         this.ctx = <Vex.Flow.SVGContext>this.renderer.getContext();
-
+        this.ctx.svg.id = "osmdSvgPage" + this.graphicalMusicPage.PageNumber;
     }
 
     public getContext(): Vex.Flow.SVGContext {
         return this.ctx;
     }
 
+    public getSvgElement(): SVGElement {
+        return this.ctx.svg;
+    }
+
     public clear(): void {
         if (!this.ctx) {
             return;
         }
-        const { svg } = this.ctx;
+        //const { svg } = this.ctx; // seems to make svg static between osmd instances.
+        const svg: SVGElement = this.ctx.svg;
         // removes all children from the SVG element,
         // effectively clearing the SVG viewport
         while (svg.lastChild) {
             svg.removeChild(svg.lastChild);
         }
+
+        // set background color if not transparent
+        if (this.rules.PageBackgroundColor) {
+            this.ctx.save();
+            // note that this will hide the cursor
+            this.ctx.setFillStyle(this.rules.PageBackgroundColor);
+            this.ctx.setStrokeStyle("#12345600"); // transparent
+
+            this.ctx.fillRect(0, 0, this.canvas.offsetWidth / this.zoom, this.canvas.offsetHeight / this.zoom);
+            this.ctx.restore();
+        }
     }
 
     public scale(k: number): void {
@@ -50,17 +85,42 @@ export class SvgVexFlowBackend extends VexFlowBackend {
         // TODO: implement this
     }
     public renderText(fontHeight: number, fontStyle: FontStyles, font: Fonts, text: string,
-                      heightInPixel: number, screenPosition: PointF2D, color: string = undefined): void {
+                      heightInPixel: number, screenPosition: PointF2D,
+                      color: string = undefined, fontFamily: string = undefined): void {
         this.ctx.save();
 
         if (color) {
             this.ctx.attributes.fill = color;
             this.ctx.attributes.stroke = color;
         }
-        this.ctx.setFont("Times New Roman", fontHeight, VexFlowConverter.fontStyle(fontStyle));
+        let fontFamilyVexFlow: string = fontFamily;
+        if (!fontFamily || fontFamily === "default") {
+            fontFamilyVexFlow = this.rules.DefaultFontFamily;
+        }
+        this.ctx.setFont(fontFamilyVexFlow, fontHeight, VexFlowConverter.fontStyle(fontStyle));
         // font size is set by VexFlow in `pt`. This overwrites the font so it's set to px instead
         this.ctx.attributes["font-size"] = `${fontHeight}px`;
         this.ctx.state["font-size"] = `${fontHeight}px`;
+        let fontWeightVexflow: string = "normal";
+        let fontStyleVexflow: string = "normal";
+        switch (fontStyle) {
+            case FontStyles.Bold:
+                fontWeightVexflow = "bold";
+                break;
+            case FontStyles.Italic:
+                fontStyleVexflow = "italic";
+                break;
+            case FontStyles.BoldItalic:
+                fontWeightVexflow = "bold";
+                fontStyleVexflow = "italic";
+                break;
+            default:
+                fontWeightVexflow = "normal";
+        }
+        this.ctx.attributes["font-weight"] = fontWeightVexflow;
+        this.ctx.state["font-weight"] = fontWeightVexflow;
+        this.ctx.attributes["font-style"] = fontStyleVexflow;
+        this.ctx.state["font-style"] = fontStyleVexflow;
         this.ctx.fillText(text, screenPosition.x, screenPosition.y + heightInPixel);
         this.ctx.restore();
     }

+ 61 - 9
src/MusicalScore/Graphical/VexFlow/VexFlowBackend.ts

@@ -1,20 +1,30 @@
-import * as Vex from "vexflow";
+import Vex from "vexflow";
 import {FontStyles} from "../../../Common/Enums/FontStyles";
 import {Fonts} from "../../../Common/Enums/Fonts";
 import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
 import {PointF2D} from "../../../Common/DataObjects/PointF2D";
+import {BackendType} from "../../../OpenSheetMusicDisplay/OSMDOptions";
+import {GraphicalMusicPage} from "../GraphicalMusicPage";
+import {EngravingRules} from "../EngravingRules";
 
 export class VexFlowBackends {
   public static CANVAS: 0;
-  public static RAPHAEL: 1;
+  public static RAPHAEL: 1; // this is currently unused in OSMD, and outdated in Vexflow.
+  // maybe SVG should be 1? but this could be a breaking change if people use numbers (2) instead of names (.SVG).
   public static SVG: 2;
-  public static VML: 3;
+  public static VML: 3; // this is currently unused in OSMD, and outdated in Vexflow
 
 }
 
 export abstract class VexFlowBackend {
 
-  public abstract initialize(container: HTMLElement): void;
+  /** The GraphicalMusicPage the backend is drawing from. Each backend only renders one GraphicalMusicPage, to which the coordinates are relative. */
+  public graphicalMusicPage: GraphicalMusicPage;
+  protected rules: EngravingRules;
+  public width: number; // read-only
+  public height: number; // read-only
+
+  public abstract initialize(container: HTMLElement, zoom: number): void;
 
   public getInnerElement(): HTMLElement {
     return this.inner;
@@ -24,26 +34,63 @@ export abstract class VexFlowBackend {
     return this.canvas;
   }
 
+  public abstract getCanvasSize(): number;
+
+  public getRenderElement(): HTMLElement {
+    //console.log("backend type: " + this.getVexflowBackendType());
+    let renderingHtmlElement: HTMLElement = this.canvas; // for SVGBackend
+    if (this.getVexflowBackendType() === Vex.Flow.Renderer.Backends.CANVAS) {
+      renderingHtmlElement = this.inner;
+      // usage in removeFromContainer:
+      // for SVG, this.canvas === this.inner, but for Canvas, removing this.canvas causes an error because it's not a child of container,
+      // so we have to remove this.inner instead.
+    }
+    return renderingHtmlElement;
+  }
+
   public getRenderer(): Vex.Flow.Renderer {
     return this.renderer;
   }
 
-  public abstract getContext(): Vex.Flow.RenderContext;
+  public removeAllChildrenFromContainer(container: HTMLElement): void {
+    while (container.children.length !== 0) {
+      container.removeChild(container.children.item(0));
+    }
+  }
+
+  // note: removing single children to remove all is error-prone, because sometimes a random SVG-child remains.
+  public removeFromContainer(container: HTMLElement): void {
+    const htmlElementToRemove: HTMLElement = this.getRenderElement();
+
+    // only remove child if the container has this child, otherwise it will throw an error.
+    for (let i: number = 0; i < container.children.length; i++) {
+      if (container.children.item(i) === htmlElementToRemove) {
+        container.removeChild(htmlElementToRemove);
+        break;
+      }
+    }
+    // there is unfortunately no built-in container.hasChild(child) method.
+  }
+
+public abstract getContext(): Vex.IRenderContext;
 
   // public abstract setWidth(width: number): void;
   // public abstract setHeight(height: number): void;
 
   public abstract scale(k: number): void;
 
-  public resize(x: number, y: number): void {
-    this.renderer.resize(x, y);
+  public resize(width: number, height: number): void {
+    this.renderer.resize(width, height);
+    this.width = width;
+    this.height = height;
   }
 
   public abstract clear(): void;
 
   public abstract translate(x: number, y: number): void;
   public abstract renderText(fontHeight: number, fontStyle: FontStyles, font: Fonts, text: string,
-                             heightInPixel: number, screenPosition: PointF2D, color?: string): void;
+                             heightInPixel: number, screenPosition: PointF2D,
+                             color?: string, fontFamily?: string): void;
   /**
    * Renders a rectangle with the given style to the screen.
    * It is given in screen coordinates.
@@ -58,7 +105,12 @@ export abstract class VexFlowBackend {
 
   public abstract renderCurve(points: PointF2D[]): void;
 
-  public abstract getBackendType(): number;
+  public abstract getVexflowBackendType(): Vex.Flow.Renderer.Backends;
+
+  /** The general type of backend: Canvas or SVG.
+   * This is not used for now (only VexflowBackendType used), but it may be useful when we don't want to use a Vexflow class.
+   */
+  public abstract getOSMDBackendType(): BackendType;
 
   protected renderer: Vex.Flow.Renderer;
   protected inner: HTMLElement;

+ 7 - 3
src/MusicalScore/Graphical/VexFlow/VexFlowContinuousDynamicExpression.ts

@@ -5,17 +5,21 @@ import { GraphicalLabel } from "../GraphicalLabel";
 import { Label } from "../../Label";
 import { TextAlignmentEnum } from "../../../Common/Enums/TextAlignment";
 import { FontStyles } from "../../../Common/Enums/FontStyles";
+import { SourceMeasure } from "../../VoiceData/SourceMeasure";
 
 /**
  * This class extends the GraphicalContinuousDynamicExpression and creates all necessary methods for drawing
  */
 export class VexFlowContinuousDynamicExpression extends GraphicalContinuousDynamicExpression {
-    constructor(continuousDynamic: ContinuousDynamicExpression, staffLine: StaffLine, textHeight?: number) {
-        super(continuousDynamic, staffLine);
+    constructor(continuousDynamic: ContinuousDynamicExpression, staffLine: StaffLine,
+                measure: SourceMeasure, textHeight?: number) {
+        super(continuousDynamic, staffLine, measure);
         if (this.IsVerbal) {
-            this.label = new GraphicalLabel(new Label(continuousDynamic.Label),
+            const sourceLabel: Label = new Label(continuousDynamic.Label);
+            this.label = new GraphicalLabel(sourceLabel,
                                             textHeight ? textHeight : this.rules.ContinuousDynamicTextHeight,
                                             TextAlignmentEnum.LeftCenter,
+                                            this.rules,
                                             this.PositionAndShape);
 
             this.label.Label.fontStyle = FontStyles.Italic;

+ 305 - 78
src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts

@@ -1,4 +1,4 @@
-import Vex = require("vexflow");
+import Vex from "vexflow";
 import {ClefEnum} from "../../VoiceData/Instructions/ClefInstruction";
 import {ClefInstruction} from "../../VoiceData/Instructions/ClefInstruction";
 import {Pitch} from "../../../Common/DataObjects/Pitch";
@@ -15,15 +15,21 @@ import {SystemLinesEnum} from "../SystemLinesEnum";
 import {FontStyles} from "../../../Common/Enums/FontStyles";
 import {Fonts} from "../../../Common/Enums/Fonts";
 import {OutlineAndFillStyleEnum, OUTLINE_AND_FILL_STYLE_DICT} from "../DrawingEnums";
-import * as log from "loglevel";
-import { ArticulationEnum, StemDirectionType } from "../../VoiceData/VoiceEntry";
+import log from "loglevel";
+import { ArticulationEnum, StemDirectionType, VoiceEntry } from "../../VoiceData/VoiceEntry";
 import { SystemLinePosition } from "../SystemLinePosition";
 import { GraphicalVoiceEntry } from "../GraphicalVoiceEntry";
 import { OrnamentEnum, OrnamentContainer } from "../../VoiceData/OrnamentContainer";
 import { Notehead, NoteHeadShape } from "../../VoiceData/Notehead";
 import { unitInPixels } from "./VexFlowMusicSheetDrawer";
 import { EngravingRules } from "../EngravingRules";
-import { Note } from "../..";
+import { Note } from "../../../MusicalScore/VoiceData/Note";
+import StaveNote = Vex.Flow.StaveNote;
+import { ArpeggioType } from "../../VoiceData/Arpeggio";
+import { TabNote } from "../../VoiceData/TabNote";
+import { PlacementEnum } from "../../VoiceData/Expressions/AbstractExpression";
+import { GraphicalStaffEntry } from "../GraphicalStaffEntry";
+import { Articulation } from "../../VoiceData/Articulation";
 
 /**
  * Helper class, which contains static methods which actually convert
@@ -55,11 +61,15 @@ export class VexFlowConverter {
     public static duration(fraction: Fraction, isTuplet: boolean): string {
       const dur: number = fraction.RealValue;
 
+      if (dur === 2) { // Breve
+        return "1/2";
+      }
+      // TODO consider long (dur=4) and maxima (dur=8), though Vexflow doesn't seem to support them
       if (dur >= 1) {
           return "w";
       } else if (dur < 1 && dur >= 0.5) {
         // change to the next higher straight note to get the correct note display type
-        if (isTuplet) {
+        if (isTuplet && dur > 0.5) {
           return "w";
         }
         return "h";
@@ -107,17 +117,46 @@ export class VexFlowConverter {
      * @param pitch
      * @returns {string[]}
      */
-    public static pitch(note: VexFlowGraphicalNote, pitch: Pitch): [string, string, ClefInstruction] {
+    public static pitch(pitch: Pitch, isRest: boolean, clef: ClefInstruction,
+                        notehead: Notehead = undefined): [string, string, ClefInstruction] {
+        //FIXME: The octave seems to need a shift of three?
+        //FIXME: Also rests seem to use different offsets depending on the clef.
+        let fixmeOffset: number = 3;
+        if (isRest) {
+            fixmeOffset = 0;
+            if (clef.ClefType === ClefEnum.F) {
+                fixmeOffset = 2;
+            }
+            if (clef.ClefType === ClefEnum.C) {
+                fixmeOffset = 2;
+            }
+            // TODO the pitch for rests will be the start position, for eights rests it will be the bottom point
+            // maybe we want to center on the display position instead of having the bottom there?
+        }
         const fund: string = NoteEnum[pitch.FundamentalNote].toLowerCase();
         const acc: string = Pitch.accidentalVexflow(pitch.Accidental);
-        // The octave seems to need a shift of three FIXME?
-        const octave: number = pitch.Octave - note.Clef().OctaveOffset + 3;
-        const notehead: Notehead = note.sourceNote.Notehead;
+        const octave: number = pitch.Octave - clef.OctaveOffset + fixmeOffset;
         let noteheadCode: string = "";
-        if (notehead !== undefined) {
+        if (notehead) {
             noteheadCode = this.NoteHeadCode(notehead);
         }
-        return [fund + "n/" + octave + noteheadCode, acc, note.Clef()];
+        return [fund + "n/" + octave + noteheadCode, acc, clef];
+    }
+
+    public static restToNotePitch(pitch: Pitch, clefType: ClefEnum): Pitch {
+        let octave: number = pitch.Octave;
+        // offsets see pitch()
+        switch (clefType) {
+            case ClefEnum.C:
+            case ClefEnum.F: {
+                octave += 2;
+                break;
+            }
+            case ClefEnum.G:
+            default:
+        }
+
+        return new Pitch(pitch.FundamentalNote, octave, AccidentalEnum.NONE);
     }
 
     /** returns the Vexflow code for a note head. Some are still unsupported, see Vexflow/tables.js */
@@ -165,14 +204,15 @@ export class VexFlowConverter {
         } */
         // VexFlow needs the notes ordered vertically in the other direction:
         const notes: GraphicalNote[] = gve.notes.reverse();
+        const rules: EngravingRules = gve.parentStaffEntry.parentMeasure.parentSourceMeasure.Rules;
 
         const baseNote: GraphicalNote = notes[0];
         let keys: string[] = [];
         const accidentals: string[] = [];
-        const frac: Fraction = baseNote.graphicalNoteLength;
+        const baseNoteLength: Fraction = baseNote.graphicalNoteLength;
         const isTuplet: boolean = baseNote.sourceNote.NoteTuplet !== undefined;
-        let duration: string = VexFlowConverter.duration(frac, isTuplet);
-        if (baseNote.sourceNote.TypeLength !== undefined && baseNote.sourceNote.TypeLength !== frac) {
+        let duration: string = VexFlowConverter.duration(baseNoteLength, isTuplet);
+        if (baseNote.sourceNote.TypeLength !== undefined && baseNote.sourceNote.TypeLength !== baseNoteLength) {
             duration = VexFlowConverter.duration(baseNote.sourceNote.TypeLength, isTuplet);
         }
         let vfClefType: string = undefined;
@@ -189,9 +229,50 @@ export class VexFlowConverter {
             // if it is a rest:
             if (note.sourceNote.isRest()) {
                 isRest = true;
-                keys = ["b/4"];
+                if (note.sourceNote.Pitch) {
+                    const restVfPitch: [string, string, ClefInstruction] = (note as VexFlowGraphicalNote).vfpitch;
+                    keys = [restVfPitch[0]];
+                    break;
+                } else {
+                    keys = ["b/4"]; // default placement
+
+                    // pause rest encircled by two beamed notes: place rest just below previous note
+                    const pauseVoiceEntry: VoiceEntry = note.parentVoiceEntry?.parentVoiceEntry;
+                    if (pauseVoiceEntry) {
+                        const neighborGSEs: GraphicalStaffEntry[] = note.parentVoiceEntry?.parentStaffEntry.parentMeasure.staffEntries;
+                        let previousVoiceEntry: VoiceEntry, followingVoiceEntry: VoiceEntry;
+                        let pauseVEIndex: number = -1;
+                        for (let i: number = 0; i < neighborGSEs.length; i++) {
+                            if (neighborGSEs[i]?.graphicalVoiceEntries[0].parentVoiceEntry === pauseVoiceEntry) {
+                                pauseVEIndex = i;
+                                break;
+                            }
+                        }
+                        if (pauseVEIndex >= 1 && (neighborGSEs.length - 1) >= (pauseVEIndex + 1)) {
+                            previousVoiceEntry = neighborGSEs[pauseVEIndex - 1]?.graphicalVoiceEntries[0]?.parentVoiceEntry;
+                            followingVoiceEntry = neighborGSEs[pauseVEIndex + 1]?.graphicalVoiceEntries[0]?.parentVoiceEntry;
+                            if (previousVoiceEntry && followingVoiceEntry) {
+                                const previousNote: Note = previousVoiceEntry.Notes[0];
+                                const followingNote: Note = followingVoiceEntry.Notes[0];
+                                if (previousNote.NoteBeam?.Notes.includes(followingNote)) {
+                                    const previousNotePitch: Pitch = previousVoiceEntry.Notes.last().Pitch;
+                                    const clef: ClefInstruction = (note as VexFlowGraphicalNote).Clef();
+                                    const vfpitch: [string, string, ClefInstruction] = VexFlowConverter.pitch(
+                                        VexFlowConverter.restToNotePitch(previousNotePitch.getTransposedPitch(-2), clef.ClefType),
+                                        false, clef, undefined);
+                                    keys = [vfpitch[0]];
+                                }
+                            }
+                        }
+                    }
+                }
+                // TODO do collision checking, place rest e.g. either below staff (A3, for stem direction below voice) or above (C5)
                 // if it is a full measure rest:
-                if (note.parentVoiceEntry.parentStaffEntry.parentMeasure.parentSourceMeasure.Duration.RealValue <= frac.RealValue) {
+                //   (a whole rest note signifies a whole measure duration, unless the time signature is longer than 4 quarter notes, e.g. 6/4 or 3/2.
+                //   Note: this should not apply to most pickup measures, e.g. with an 8th pickup measure in a 3/4 time signature)
+                // const measureDuration: number = note.sourceNote.SourceMeasure.Duration.RealValue;
+                const isWholeMeasureRest: boolean =  baseNoteLength.RealValue === note.sourceNote.SourceMeasure.ActiveTimeSignature.RealValue;
+                if (isWholeMeasureRest) {
                     keys = ["d/5"];
                     duration = "w";
                     numDots = 0;
@@ -199,9 +280,31 @@ export class VexFlowConverter {
                     // https://github.com/0xfe/vexflow/issues/579 The author reports that he needs to add some negative x shift
                     // if the measure has no modifiers.
                     alignCenter = true;
-                    xShift = EngravingRules.Rules.WholeRestXShiftVexflow * unitInPixels; // TODO find way to make dependent on the modifiers
+                    xShift = rules.WholeRestXShiftVexflow * unitInPixels; // TODO find way to make dependent on the modifiers
                     // affects VexFlowStaffEntry.calculateXPosition()
                 }
+                if (note.sourceNote.ParentStaff.Voices.length > 1) {
+                    let visibleVoiceEntries: number = 0;
+                    //Find all visible voice entries (don't want invisible rests/notes causing visible shift)
+                    for (let idx: number = 0; idx < note.sourceNote.ParentStaffEntry.VoiceEntries.length ; idx++) {
+                        if (note.sourceNote.ParentStaffEntry.VoiceEntries[idx].Notes[0].PrintObject) {
+                            visibleVoiceEntries++;
+                        }
+                    }
+                    //If we have more than one visible voice entry, shift the rests so no collision occurs
+                    if (visibleVoiceEntries > 1) {
+                        switch (note.sourceNote.ParentVoiceEntry?.ParentVoice?.VoiceId) {
+                            case 1:
+                                keys = ["e/5"];
+                                break;
+                            case 2:
+                                keys = ["f/4"];
+                                break;
+                            default:
+                                break;
+                        }
+                    }
+                }
                 break;
             }
 
@@ -235,7 +338,7 @@ export class VexFlowConverter {
 
         let vfnote: Vex.Flow.StaveNote;
 
-        const vfnoteStruct: Object = {
+        const vfnoteStruct: any = {
             align_center: alignCenter,
             auto_stem: true,
             clef: vfClefType,
@@ -246,8 +349,8 @@ export class VexFlowConverter {
 
         const firstNote: Note = gve.notes[0].sourceNote;
         if (firstNote.IsCueNote) {
-            (<any>vfnoteStruct).glyph_font_scale = Vex.Flow.DEFAULT_NOTATION_FONT_SCALE * Vex.Flow.GraceNote.SCALE;
-            (<any>vfnoteStruct).stroke_px = Vex.Flow.GraceNote.LEDGER_LINE_OFFSET;
+            vfnoteStruct.glyph_font_scale = Vex.Flow.DEFAULT_NOTATION_FONT_SCALE * Vex.Flow.GraceNote.SCALE;
+            vfnoteStruct.stroke_px = Vex.Flow.GraceNote.LEDGER_LINE_OFFSET;
         }
 
         if (gve.parentVoiceEntry.IsGrace || gve.notes[0].sourceNote.IsCueNote) {
@@ -255,17 +358,22 @@ export class VexFlowConverter {
         } else {
             vfnote = new Vex.Flow.StaveNote(vfnoteStruct);
         }
-
-        // half note tremolo: set notehead to half note (Vexflow otherwise takes the notehead from duration):
-        if (firstNote.Length.RealValue === 0.25 && firstNote.Notehead && firstNote.Notehead.Filled === false) {
-            const keyProps: Object[] = vfnote.getKeyProps();
-            for (let i: number = 0; i < keyProps.length; i++) {
-                (<any>keyProps[i]).code = "v81";
+        if (rules.LedgerLineWidth || rules.LedgerLineStrokeStyle) {
+            // FIXME should probably use vfnote.setLedgerLineStyle. this doesn't seem to do anything.
+            // however, this is also set in VexFlowVoiceEntry.color() anyways.
+            if (!((vfnote as any).ledgerLineStyle)) {
+                (vfnote as any).ledgerLineStyle = {};
+            }
+            if (rules.LedgerLineWidth) {
+                (vfnote as any).ledgerLineStyle.lineWidth = rules.LedgerLineWidth;
+            }
+            if (rules.LedgerLineStrokeStyle) {
+                (vfnote as any).ledgerLineStyle.strokeStyle = rules.LedgerLineStrokeStyle;
             }
         }
 
-        if (EngravingRules.Rules.ColoringEnabled) {
-            const defaultColorStem: string = EngravingRules.Rules.DefaultColorStem;
+        if (rules.ColoringEnabled) {
+            const defaultColorStem: string = rules.DefaultColorStem;
             let stemColor: string = gve.parentVoiceEntry.StemColor;
             if (!stemColor && defaultColorStem) {
                 stemColor = defaultColorStem;
@@ -275,7 +383,7 @@ export class VexFlowConverter {
             if (stemColor) {
                 gve.parentVoiceEntry.StemColor = stemColor;
                 vfnote.setStemStyle(stemStyle);
-                if (vfnote.flag && EngravingRules.Rules.ColorFlags) {
+                if (vfnote.flag && rules.ColorFlags) {
                     vfnote.setFlagStyle(stemStyle);
                 }
             }
@@ -288,7 +396,7 @@ export class VexFlowConverter {
             // when the stem is connected to a beamed main note (e.g. Haydn Concertante bar 57)
             gve.parentVoiceEntry.WantedStemDirection = gve.notes[0].sourceNote.NoteBeam.Notes[0].ParentVoiceEntry.WantedStemDirection;
         }
-        if (gve.parentVoiceEntry !== undefined) {
+        if (gve.parentVoiceEntry) {
             const wantedStemDirection: StemDirectionType = gve.parentVoiceEntry.WantedStemDirection;
             switch (wantedStemDirection) {
                 case(StemDirectionType.Up):
@@ -316,34 +424,62 @@ export class VexFlowConverter {
                 }
                 vfnote.addAccidental(i, new Vex.Flow.Accidental(accidentals[i])); // normal accidental
             }
+
+            // add Tremolo strokes (only single note tremolos for now, Vexflow doesn't have beams for two-note tremolos yet)
+            const tremoloStrokes: number = notes[i].sourceNote.TremoloStrokes;
+            if (tremoloStrokes > 0) {
+                const tremolo: Vex.Flow.Tremolo = new Vex.Flow.Tremolo(tremoloStrokes);
+                (tremolo as any).extra_stroke_scale = rules.TremoloStrokeScale;
+                (tremolo as any).y_spacing_scale = rules.TremoloYSpacingScale;
+                vfnote.addModifier(i, tremolo);
+            }
+        }
+
+        // half note tremolo: set notehead to half note (Vexflow otherwise takes the notehead from duration) (Hack)
+        if (firstNote.Length.RealValue === 0.25 && firstNote.Notehead && firstNote.Notehead.Filled === false) {
+            const keyProps: Object[] = vfnote.getKeyProps();
+            for (let i: number = 0; i < keyProps.length; i++) {
+                (<any>keyProps[i]).code = "v81";
+            }
         }
+
         for (let i: number = 0, len: number = numDots; i < len; ++i) {
             vfnote.addDotToAll();
         }
         return vfnote;
     }
 
-    public static generateArticulations(vfnote: Vex.Flow.StemmableNote, articulations: ArticulationEnum[]): void {
-        if (vfnote === undefined || vfnote.getAttribute("type") === "GhostNote") {
+    public static generateArticulations(vfnote: Vex.Flow.StemmableNote, articulations: Articulation[],
+                                        rules: EngravingRules): void {
+        if (!vfnote || vfnote.getAttribute("type") === "GhostNote") {
             return;
         }
-        // Articulations:
-        let vfArtPosition: number = Vex.Flow.Modifier.Position.ABOVE;
-
-        if (vfnote.getStemDirection() === Vex.Flow.Stem.UP) {
-            vfArtPosition = Vex.Flow.Modifier.Position.BELOW;
-        }
 
         for (const articulation of articulations) {
-            // tslint:disable-next-line:switch-default
+            let vfArtPosition: number = Vex.Flow.Modifier.Position.ABOVE;
+
+            if (vfnote.getStemDirection() === Vex.Flow.Stem.UP) {
+                vfArtPosition = Vex.Flow.Modifier.Position.BELOW;
+            }
             let vfArt: Vex.Flow.Articulation = undefined;
-            switch (articulation) {
+            const articulationEnum: ArticulationEnum = articulation.articulationEnum;
+            if (rules.ArticulationPlacementFromXML) {
+                if (articulation.placement === PlacementEnum.Above) {
+                    vfArtPosition = Vex.Flow.Modifier.Position.ABOVE;
+                } else if (articulation.placement === PlacementEnum.Below) {
+                    vfArtPosition = Vex.Flow.Modifier.Position.BELOW;
+                } // else if undefined: don't change
+            }
+            switch (articulationEnum) {
                 case ArticulationEnum.accent: {
                     vfArt = new Vex.Flow.Articulation("a>");
                     break;
                 }
                 case ArticulationEnum.downbow: {
                     vfArt = new Vex.Flow.Articulation("am");
+                    if (articulation.placement === undefined) { // downbow/upbow should be above by default
+                        vfArtPosition = Vex.Flow.Modifier.Position.ABOVE;
+                    }
                     break;
                 }
                 case ArticulationEnum.fermata: {
@@ -351,6 +487,14 @@ export class VexFlowConverter {
                     vfArtPosition = Vex.Flow.Modifier.Position.ABOVE;
                     break;
                 }
+                case ArticulationEnum.marcatodown: {
+                    vfArt = new Vex.Flow.Articulation("a|"); // Vexflow only knows marcato up, so we use a down stroke here.
+                    break;
+                }
+                case ArticulationEnum.marcatoup: {
+                    vfArt = new Vex.Flow.Articulation("a^");
+                    break;
+                }
                 case ArticulationEnum.invertedfermata: {
                     vfArt = new Vex.Flow.Articulation("a@u");
                     vfArtPosition = Vex.Flow.Modifier.Position.BELOW;
@@ -360,6 +504,10 @@ export class VexFlowConverter {
                     vfArt = new Vex.Flow.Articulation("a+");
                     break;
                 }
+                case ArticulationEnum.naturalharmonic: {
+                    vfArt = new Vex.Flow.Articulation("ah");
+                    break;
+                }
                 case ArticulationEnum.snappizzicato: {
                     vfArt = new Vex.Flow.Articulation("ao");
                     break;
@@ -378,6 +526,9 @@ export class VexFlowConverter {
                 }
                 case ArticulationEnum.upbow: {
                     vfArt = new Vex.Flow.Articulation("a|");
+                    if (articulation.placement === undefined) { // downbow/upbow should be above by default
+                        vfArtPosition = Vex.Flow.Modifier.Position.ABOVE;
+                    }
                     break;
                 }
                 case ArticulationEnum.strongaccent: {
@@ -388,16 +539,16 @@ export class VexFlowConverter {
                     break;
                 }
             }
-            if (vfArt !== undefined) {
+            if (vfArt) {
                 vfArt.setPosition(vfArtPosition);
-                vfnote.addModifier(0, vfArt);
+                (vfnote as StaveNote).addModifier(0, vfArt);
             }
         }
     }
 
     public static generateOrnaments(vfnote: Vex.Flow.StemmableNote, oContainer: OrnamentContainer): void {
         let vfPosition: number = Vex.Flow.Modifier.Position.ABOVE;
-        if (vfnote.getStemDirection() === Vex.Flow.Stem.UP) {
+        if (oContainer.placement === PlacementEnum.Below) {
             vfPosition = Vex.Flow.Modifier.Position.BELOW;
         }
 
@@ -414,7 +565,7 @@ export class VexFlowConverter {
                 break;
             }
             case OrnamentEnum.InvertedMordent: {
-                vfOrna = new Vex.Flow.Ornament("mordent_inverted");
+                vfOrna = new Vex.Flow.Ornament("mordent"); // Vexflow uses baroque, not MusicXML definition
                 vfOrna.setDelayed(false);
                 break;
             }
@@ -424,7 +575,7 @@ export class VexFlowConverter {
                 break;
             }
             case OrnamentEnum.Mordent: {
-                vfOrna = new Vex.Flow.Ornament("mordent");
+                vfOrna = new Vex.Flow.Ornament("mordent_inverted");
                 vfOrna.setDelayed(false);
                 break;
             }
@@ -443,19 +594,112 @@ export class VexFlowConverter {
                 return;
             }
         }
-        if (vfOrna !== undefined) {
+        if (vfOrna) {
             if (oContainer.AccidentalBelow !== AccidentalEnum.NONE) {
                 vfOrna.setLowerAccidental(Pitch.accidentalVexflow(oContainer.AccidentalBelow));
             }
             if (oContainer.AccidentalAbove !== AccidentalEnum.NONE) {
                 vfOrna.setUpperAccidental(Pitch.accidentalVexflow(oContainer.AccidentalAbove));
             }
-            vfOrna.setPosition(vfPosition);
-            vfnote.addModifier(0, vfOrna);
+            vfOrna.setPosition(vfPosition); // Vexflow draws it above right now in any case, never below
+            (vfnote as StaveNote).addModifier(0, vfOrna);
+        }
+    }
+
+    public static StrokeTypeFromArpeggioType(arpeggioType: ArpeggioType): Vex.Flow.Stroke.Type {
+        switch (arpeggioType) {
+            case ArpeggioType.ARPEGGIO_DIRECTIONLESS:
+                return Vex.Flow.Stroke.Type.ARPEGGIO_DIRECTIONLESS;
+            case ArpeggioType.BRUSH_DOWN:
+                return Vex.Flow.Stroke.Type.BRUSH_UP; // TODO somehow up and down are mixed up in Vexflow right now
+            case ArpeggioType.BRUSH_UP:
+                return Vex.Flow.Stroke.Type.BRUSH_DOWN; // TODO somehow up and down are mixed up in Vexflow right now
+            case ArpeggioType.RASQUEDO_DOWN:
+                return Vex.Flow.Stroke.Type.RASQUEDO_UP;
+            case ArpeggioType.RASQUEDO_UP:
+                return Vex.Flow.Stroke.Type.RASQUEDO_DOWN;
+            case ArpeggioType.ROLL_DOWN:
+                return Vex.Flow.Stroke.Type.ROLL_UP; // TODO somehow up and down are mixed up in Vexflow right now
+            case ArpeggioType.ROLL_UP:
+                return Vex.Flow.Stroke.Type.ROLL_DOWN; // TODO somehow up and down are mixed up in Vexflow right now
+            default:
+                return Vex.Flow.Stroke.Type.ARPEGGIO_DIRECTIONLESS;
         }
     }
 
     /**
+     * Convert a set of GraphicalNotes to a VexFlow StaveNote
+     * @param notes form a chord on the staff
+     * @returns {Vex.Flow.StaveNote}
+     */
+    public static CreateTabNote(gve: GraphicalVoiceEntry): Vex.Flow.TabNote {
+        const tabPositions: {str: number, fret: number}[] = [];
+        const notes: GraphicalNote[] = gve.notes.reverse();
+        const tabPhrases: { type: number, text: string, width: number }[] = [];
+        const frac: Fraction = gve.notes[0].graphicalNoteLength;
+        const isTuplet: boolean = gve.notes[0].sourceNote.NoteTuplet !== undefined;
+        let duration: string = VexFlowConverter.duration(frac, isTuplet);
+        let numDots: number = 0;
+        let tabVibrato: boolean = false;
+        for (const note of gve.notes) {
+            const tabNote: TabNote = note.sourceNote as TabNote;
+            const tabPosition: {str: number, fret: number} = {str: tabNote.StringNumber, fret: tabNote.FretNumber};
+            tabPositions.push(tabPosition);
+            if (tabNote.BendArray) {
+                tabNote.BendArray.forEach( function( bend: {bendalter: number, direction: string} ): void {
+                    let phraseText: string;
+                    const phraseStep: number = bend.bendalter - tabPosition.fret;
+                    if (phraseStep > 1) {
+                        phraseText = "Full";
+                    } else if (phraseStep === 1) {
+                        phraseText = "1/2";
+                    } else {
+                        phraseText = "1/4";
+                    }
+                    if (bend.direction === "up") {
+                        tabPhrases.push({type: Vex.Flow.Bend.UP, text: phraseText, width: 10});
+                    } else {
+                        tabPhrases.push({type: Vex.Flow.Bend.DOWN, text: phraseText, width: 10});
+                    }
+                });
+            }
+
+            if (tabNote.VibratoStroke) {
+                tabVibrato = true;
+            }
+
+            if (numDots < note.numberOfDots) {
+                numDots = note.numberOfDots;
+            }
+        }
+        for (let i: number = 0, len: number = numDots; i < len; ++i) {
+            duration += "d";
+        }
+
+        const vfnote: Vex.Flow.TabNote = new Vex.Flow.TabNote({
+            duration: duration,
+            positions: tabPositions,
+        });
+
+        for (let i: number = 0, len: number = notes.length; i < len; i += 1) {
+            (notes[i] as VexFlowGraphicalNote).setIndex(vfnote, i);
+        }
+
+        tabPhrases.forEach(function(phrase: { type: number, text: string, width: number }): void {
+            if (phrase.type === Vex.Flow.Bend.UP) {
+                vfnote.addModifier (new Vex.Flow.Bend(phrase.text, false));
+            } else {
+                vfnote.addModifier (new Vex.Flow.Bend(phrase.text, true));
+            }
+        });
+        if (tabVibrato) {
+            vfnote.addModifier(new Vex.Flow.Vibrato());
+        }
+
+        return vfnote;
+    }
+
+    /**
      * Convert a ClefInstruction to a string represention of a clef type in VexFlow.
      *
      * @param clef The OSMD object to be converted representing the clef
@@ -542,9 +786,12 @@ export class VexFlowConverter {
 
             // TAB Clef
             case ClefEnum.TAB:
-                type = "tab";
+                // only used currently for creating the notes in the normal stave: There we need a normal treble clef
+                type = "treble";
                 break;
             default:
+                log.info("bad clef type: " + clef.ClefType);
+                type = "treble";
         }
 
         // annotations in vexflow don't allow bass and 8va. No matter the offset :(
@@ -585,7 +832,7 @@ export class VexFlowConverter {
      * @returns {string}
      */
     public static keySignature(key: KeyInstruction): string {
-        if (key === undefined) {
+        if (!key) {
             return undefined;
         }
         let ret: string;
@@ -641,10 +888,11 @@ export class VexFlowConverter {
      * @param font
      * @returns {string}
      */
-    public static font(fontSize: number, fontStyle: FontStyles = FontStyles.Regular, font: Fonts = Fonts.TimesNewRoman): string {
+    public static font(fontSize: number, fontStyle: FontStyles = FontStyles.Regular,
+                       font: Fonts = Fonts.TimesNewRoman, rules: EngravingRules, fontFamily: string = undefined): string {
         let style: string = "normal";
         let weight: string = "normal";
-        const family: string = "'Times New Roman'";
+        let family: string = `'${rules.DefaultFontFamily}'`; // default "'Times New Roman'"
 
         switch (fontStyle) {
             case FontStyles.Bold:
@@ -664,15 +912,18 @@ export class VexFlowConverter {
                 break;
         }
 
-        switch (font) {
+        switch (font) { // currently not used
             case Fonts.Kokila:
                 // TODO Not Supported
                 break;
             default:
         }
 
+        if (fontFamily && fontFamily !== "default") {
+            family = `'${fontFamily}'`;
+        }
 
-        return  style + " " + weight + " " + Math.floor(fontSize) + "px " + family;
+        return style + " " + weight + " " + Math.floor(fontSize) + "px " + family;
     }
 
     /**
@@ -703,28 +954,4 @@ export class VexFlowConverter {
     }
 }
 
-export enum VexFlowRepetitionType {
-    NONE = 1,         // no coda or segno
-    CODA_LEFT = 2,    // coda at beginning of stave
-    CODA_RIGHT = 3,   // coda at end of stave
-    SEGNO_LEFT = 4,   // segno at beginning of stave
-    SEGNO_RIGHT = 5,  // segno at end of stave
-    DC = 6,           // D.C. at end of stave
-    DC_AL_CODA = 7,   // D.C. al coda at end of stave
-    DC_AL_FINE = 8,   // D.C. al Fine end of stave
-    DS = 9,           // D.S. at end of stave
-    DS_AL_CODA = 10,  // D.S. al coda at end of stave
-    DS_AL_FINE = 11,  // D.S. al Fine at end of stave
-    FINE = 12,        // Fine at end of stave
-}
-
-export enum VexFlowBarlineType {
-    SINGLE = 1,
-    DOUBLE = 2,
-    END = 3,
-    REPEAT_BEGIN = 4,
-    REPEAT_END = 5,
-    REPEAT_BOTH = 6,
-    NONE = 7,
-}
 

+ 43 - 17
src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalNote.ts

@@ -1,4 +1,4 @@
-import Vex = require("vexflow");
+import Vex from "vexflow";
 import {GraphicalNote} from "../GraphicalNote";
 import {Note} from "../../VoiceData/Note";
 import {ClefInstruction} from "../../VoiceData/Instructions/ClefInstruction";
@@ -7,6 +7,7 @@ import {Pitch} from "../../../Common/DataObjects/Pitch";
 import {Fraction} from "../../../Common/DataObjects/Fraction";
 import {OctaveEnum, OctaveShift} from "../../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
 import { GraphicalVoiceEntry } from "../GraphicalVoiceEntry";
+import { KeyInstruction } from "../../VoiceData/Instructions/KeyInstruction";
 
 /**
  * The VexFlow version of a [[GraphicalNote]].
@@ -19,8 +20,8 @@ export class VexFlowGraphicalNote extends GraphicalNote {
         this.octaveShift = octaveShift;
         if (note.Pitch) {
             // TODO: Maybe shift to Transpose function when available
-            const drawPitch: Pitch = OctaveShift.getPitchFromOctaveShift(note.Pitch, octaveShift);
-            this.vfpitch = VexFlowConverter.pitch(this, drawPitch);
+            const drawPitch: Pitch = note.isRest() ? note.Pitch : OctaveShift.getPitchFromOctaveShift(note.Pitch, octaveShift);
+            this.vfpitch = VexFlowConverter.pitch(drawPitch, note.isRest(), this.clef, this.sourceNote.Notehead);
             this.vfpitch[1] = undefined;
         }
     }
@@ -29,7 +30,7 @@ export class VexFlowGraphicalNote extends GraphicalNote {
     // The pitch of this note as given by VexFlowConverter.pitch
     public vfpitch: [string, string, ClefInstruction];
     // The corresponding VexFlow StaveNote (plus its index in the chord)
-    public vfnote: [Vex.Flow.StaveNote, number];
+    public vfnote: [Vex.Flow.StemmableNote, number];
     // The current clef
     private clef: ClefInstruction;
 
@@ -38,18 +39,27 @@ export class VexFlowGraphicalNote extends GraphicalNote {
      * This is called by VexFlowGraphicalSymbolFactory.addGraphicalAccidental.
      * @param pitch
      */
-    public setPitch(pitch: Pitch): void {
-        if (this.vfnote) {
-            const acc: string = Pitch.accidentalVexflow(pitch.Accidental);
-            if (acc) {
-                alert(acc);
-                this.vfnote[0].addAccidental(this.vfnote[1], new Vex.Flow.Accidental(acc));
-            }
-        } else {
-            // revert octave shift, as the placement of the note is independent of octave brackets
-            const drawPitch: Pitch = OctaveShift.getPitchFromOctaveShift(pitch, this.octaveShift);
-            this.vfpitch = VexFlowConverter.pitch(this, drawPitch);
-        }
+    public setAccidental(pitch: Pitch): void {
+        // if (this.vfnote) {
+        //     let pitchAcc: AccidentalEnum = pitch.Accidental;
+        //     const acc: string = Pitch.accidentalVexflow(pitch.Accidental);
+        //     if (acc) {
+        //         alert(acc);
+        //         this.vfnote[0].addAccidental(this.vfnote[1], new Vex.Flow.Accidental(acc));
+        //     }
+        // } else {
+        // revert octave shift, as the placement of the note is independent of octave brackets
+        const drawPitch: Pitch = OctaveShift.getPitchFromOctaveShift(pitch, this.octaveShift);
+        // recalculate the pitch, and this time don't ignore the accidental:
+        this.vfpitch = VexFlowConverter.pitch(drawPitch, this.sourceNote.isRest(), this.clef, this.sourceNote.Notehead);
+        //}
+    }
+    public Transpose(keyInstruction: KeyInstruction, activeClef: ClefInstruction, halfTones: number, octaveEnum: OctaveEnum): Pitch {
+        const tranposedPitch: Pitch = super.Transpose(keyInstruction, activeClef, halfTones, octaveEnum);
+        const drawPitch: Pitch = OctaveShift.getPitchFromOctaveShift(tranposedPitch, this.octaveShift);
+        this.vfpitch = VexFlowConverter.pitch(drawPitch, this.sourceNote.isRest(), this.clef, this.sourceNote.Notehead);
+        this.vfpitch[1] = undefined;
+        return drawPitch;
     }
 
     /**
@@ -57,7 +67,7 @@ export class VexFlowGraphicalNote extends GraphicalNote {
      * @param note
      * @param index
      */
-    public setIndex(note: Vex.Flow.StaveNote, index: number): void {
+    public setIndex(note: Vex.Flow.StemmableNote, index: number): void {
         this.vfnote = [note, index];
     }
 
@@ -67,4 +77,20 @@ export class VexFlowGraphicalNote extends GraphicalNote {
     public Clef(): ClefInstruction {
         return this.clef;
     }
+
+    /**
+     * Gets the id of the SVGGElement containing this note, given the SVGRenderer is used.
+     * This is for low-level rendering hacks and should be used with caution.
+     */
+    public getSVGId(): string {
+        return this.vfnote[0].getAttribute("id");
+    }
+
+    /**
+     * Gets the SVGGElement containing this note, given the SVGRenderer is used.
+     * This is for low-level rendering hacks and should be used with caution.
+     */
+    public getSVGGElement(): SVGGElement {
+        return this.vfnote[0].getAttribute("el");
+    }
 }

+ 57 - 20
src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalSymbolFactory.ts

@@ -1,11 +1,9 @@
-import Vex = require("vexflow");
+import Vex from "vexflow";
 import {IGraphicalSymbolFactory} from "../../Interfaces/IGraphicalSymbolFactory";
-import {GraphicalMusicPage} from "../GraphicalMusicPage";
 import {MusicSystem} from "../MusicSystem";
 import {VexFlowMusicSystem} from "./VexFlowMusicSystem";
 import {Staff} from "../../VoiceData/Staff";
 import {StaffLine} from "../StaffLine";
-import {VexFlowStaffLine} from "./VexFlowStaffLine";
 import {SourceMeasure} from "../../VoiceData/SourceMeasure";
 import {GraphicalMeasure} from "../GraphicalMeasure";
 import {VexFlowMeasure} from "./VexFlowMeasure";
@@ -27,6 +25,10 @@ import { GraphicalVoiceEntry } from "../GraphicalVoiceEntry";
 import { VoiceEntry } from "../../VoiceData/VoiceEntry";
 import { VexFlowVoiceEntry } from "./VexFlowVoiceEntry";
 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 {
     /**
@@ -36,8 +38,8 @@ export class VexFlowGraphicalSymbolFactory implements IGraphicalSymbolFactory {
      * @param systemIndex
      * @returns {VexFlowMusicSystem}
      */
-    public createMusicSystem(page: GraphicalMusicPage, systemIndex: number): MusicSystem {
-        return new VexFlowMusicSystem(page, systemIndex);
+    public createMusicSystem(systemIndex: number, rules: EngravingRules): MusicSystem {
+        return new VexFlowMusicSystem(systemIndex, rules);
     }
 
     /**
@@ -56,8 +58,28 @@ export class VexFlowGraphicalSymbolFactory implements IGraphicalSymbolFactory {
      * @param staff
      * @returns {VexFlowMeasure}
      */
-    public createGraphicalMeasure(sourceMeasure: SourceMeasure, staff: Staff): GraphicalMeasure {
-        return new VexFlowMeasure(staff, undefined, sourceMeasure);
+    public createGraphicalMeasure(sourceMeasure: SourceMeasure, staff: Staff, isTabMeasure: boolean = false): GraphicalMeasure {
+        return new VexFlowMeasure(staff, sourceMeasure, undefined);
+    }
+
+    /**
+     * 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
+     * @returns {VexFlowTabMeasure}
+     */
+    public createTabStaffMeasure(sourceMeasure: SourceMeasure, staff: Staff): GraphicalMeasure {
+        return new VexFlowTabMeasure(staff, sourceMeasure);
     }
 
     /**
@@ -66,7 +88,7 @@ export class VexFlowGraphicalSymbolFactory implements IGraphicalSymbolFactory {
      * @returns {VexFlowMeasure}
      */
     public createExtraGraphicalMeasure(staffLine: StaffLine): GraphicalMeasure {
-        return new VexFlowMeasure(staffLine.ParentStaff, staffLine);
+        return new VexFlowMeasure(staffLine.ParentStaff, undefined, staffLine);
     }
 
     /**
@@ -121,7 +143,7 @@ export class VexFlowGraphicalSymbolFactory implements IGraphicalSymbolFactory {
     public addGraphicalAccidental(graphicalNote: GraphicalNote, pitch: Pitch): void {
         const note: VexFlowGraphicalNote = (graphicalNote as VexFlowGraphicalNote);
         // accidental is added in setPitch
-        note.setPitch(pitch);
+        note.setAccidental(pitch);
     }
 
     /**
@@ -155,17 +177,32 @@ export class VexFlowGraphicalSymbolFactory implements IGraphicalSymbolFactory {
      * @param graphicalStaffEntry
      * @param transposeHalftones
      */
-    public createChordSymbol(sourceStaffEntry: SourceStaffEntry, graphicalStaffEntry: GraphicalStaffEntry, transposeHalftones: number): void {
-      const graphicalChordSymbolContainer: GraphicalChordSymbolContainer =
-        new GraphicalChordSymbolContainer(sourceStaffEntry.ChordContainer,
-                                          graphicalStaffEntry.PositionAndShape,
-                                          EngravingRules.Rules.ChordSymbolTextHeight,
-                                          transposeHalftones);
-      const graphicalLabel: GraphicalLabel = graphicalChordSymbolContainer.GetGraphicalLabel;
-      graphicalLabel.PositionAndShape.RelativePosition.y -= EngravingRules.Rules.ChordSymbolYOffset;
-      graphicalLabel.setLabelPositionAndShapeBorders();
-      graphicalChordSymbolContainer.PositionAndShape.calculateBoundingBox();
-      graphicalStaffEntry.graphicalChordContainer = graphicalChordSymbolContainer;
+    public createChordSymbols(  sourceStaffEntry: SourceStaffEntry,
+                                graphicalStaffEntry: GraphicalStaffEntry,
+                                keyInstruction: KeyInstruction,
+                                transposeHalftones: number): void {
+        const rules: EngravingRules = graphicalStaffEntry.parentMeasure.parentSourceMeasure.Rules;
+        let xShift: number = 0;
+        const chordSymbolSpacing: number = rules.ChordSymbolXSpacing;
+        for (const chordSymbolContainer of sourceStaffEntry.ChordContainers) {
+            const graphicalChordSymbolContainer: GraphicalChordSymbolContainer =
+              new GraphicalChordSymbolContainer(chordSymbolContainer,
+                                                graphicalStaffEntry.PositionAndShape,
+                                                rules.ChordSymbolTextHeight,
+                                                keyInstruction,
+                                                transposeHalftones,
+                                                graphicalStaffEntry.parentMeasure.parentSourceMeasure.Rules // TODO undefined sometimes
+                                                );
+            const graphicalLabel: GraphicalLabel = graphicalChordSymbolContainer.GetGraphicalLabel;
+            graphicalLabel.PositionAndShape.RelativePosition.y -= rules.ChordSymbolYOffset;
+            graphicalLabel.PositionAndShape.RelativePosition.x += xShift;
+            // TODO check for available space until next staffEntry or chord symbol (x direction)
+            graphicalLabel.setLabelPositionAndShapeBorders();
+            graphicalChordSymbolContainer.PositionAndShape.calculateBoundingBox();
+            graphicalStaffEntry.graphicalChordContainers.push(graphicalChordSymbolContainer);
+
+            xShift += graphicalLabel.PositionAndShape.Size.width + chordSymbolSpacing;
+        }
     }
 
     /**

+ 3 - 1
src/MusicalScore/Graphical/VexFlow/VexFlowInstantaneousDynamicExpression.ts

@@ -11,9 +11,11 @@ export class VexFlowInstantaneousDynamicExpression extends GraphicalInstantaneou
     constructor(instantaneousDynamicExpression: InstantaneousDynamicExpression, staffLine: StaffLine, measure: GraphicalMeasure) {
         super(instantaneousDynamicExpression, staffLine, measure);
 
-        this.label = new GraphicalLabel(new Label(this.Expression),
+        const sourceLabel: Label = new Label(this.Expression);
+        this.label = new GraphicalLabel(sourceLabel,
                                         this.rules.ContinuousDynamicTextHeight,
                                         TextAlignmentEnum.CenterCenter,
+                                        this.rules,
                                         this.PositionAndShape);
 
         this.label.Label.fontStyle = FontStyles.BoldItalic;

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

@@ -1,4 +1,4 @@
-import Vex = require("vexflow");
+import Vex from "vexflow";
 import { VexFlowInstrumentBracket } from "./VexFlowInstrumentBracket";
 import { VexFlowStaffLine } from "./VexFlowStaffLine";
 

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

@@ -1,4 +1,4 @@
-import Vex = require("vexflow");
+import Vex from "vexflow";
 import { GraphicalObject } from "../GraphicalObject";
 import { VexFlowStaffLine } from "./VexFlowStaffLine";
 import { BoundingBox } from "../BoundingBox";
@@ -24,7 +24,7 @@ export class VexFlowInstrumentBracket extends GraphicalObject {
      * Render the bracket using the given backend
      * @param ctx Render Vexflow context
      */
-    public draw(ctx: Vex.Flow.RenderContext): void {
+    public draw(ctx: Vex.IRenderContext): void {
         // Draw vexflow brace. This sets the positions inside the connector.
         this.vexflowConnector.setContext(ctx).draw();
         // Set bounding box

Datei-Diff unterdrückt, da er zu groß ist
+ 437 - 108
src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts


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

@@ -0,0 +1,155 @@
+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();
+
+        this.multiRestElement = new Vex.Flow.MultiMeasureRest(sourceMeasure.multipleRestMeasures, {
+            // number_line: 3
+        });
+    }
+
+    /**
+     * 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 {
+        // like most of the following methods, not necessary / can be simplified for MultiRestMeasure.
+    }
+
+    /**
+     * Returns all the voices that are present in this measure
+     */
+    public getVoicesWithinMeasure(): Voice[] {
+        return []; // we should still return a list here, not undefined, i guess.
+    }
+
+    /**
+     * 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;
+    }
+}

+ 430 - 182
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

@@ -16,12 +16,12 @@ import { OctaveEnum, OctaveShift } from "../../VoiceData/Expressions/ContinuousE
 import { Fraction } from "../../../Common/DataObjects/Fraction";
 import { LyricWord } from "../../VoiceData/Lyrics/LyricsWord";
 import { OrnamentContainer } from "../../VoiceData/OrnamentContainer";
-import { ArticulationEnum } from "../../VoiceData/VoiceEntry";
+import { Articulation } from "../../VoiceData/Articulation";
 import { Tuplet } from "../../VoiceData/Tuplet";
 import { VexFlowMeasure } from "./VexFlowMeasure";
 import { VexFlowTextMeasurer } from "./VexFlowTextMeasurer";
-import Vex = require("vexflow");
-import * as log from "loglevel";
+import Vex from "vexflow";
+import log from "loglevel";
 import { unitInPixels } from "./VexFlowMusicSheetDrawer";
 import { VexFlowGraphicalNote } from "./VexFlowGraphicalNote";
 import { TechnicalInstruction } from "../../VoiceData/Instructions/TechnicalInstruction";
@@ -38,36 +38,60 @@ import { Slur } from "../../VoiceData/Expressions/ContinuousExpressions/Slur";
 // import { VexFlowStaffLine } from "./VexFlowStaffLine";
 // import { VexFlowVoiceEntry } from "./VexFlowVoiceEntry";
 */
-import { EngravingRules } from "../EngravingRules";
 import { PointF2D } from "../../../Common/DataObjects/PointF2D";
 import { TextAlignmentEnum, TextAlignment } from "../../../Common/Enums/TextAlignment";
 import { GraphicalSlur } from "../GraphicalSlur";
 import { BoundingBox } from "../BoundingBox";
 import { ContinuousDynamicExpression } from "../../VoiceData/Expressions/ContinuousExpressions/ContinuousDynamicExpression";
 import { VexFlowContinuousDynamicExpression } from "./VexFlowContinuousDynamicExpression";
-import { InstantaneousTempoExpression } from "../../VoiceData/Expressions";
+import { InstantaneousTempoExpression } from "../../VoiceData/Expressions/InstantaneousTempoExpression";
+import { AlignRestOption } from "../../../OpenSheetMusicDisplay/OSMDOptions";
+import { VexFlowStaffLine } from "./VexFlowStaffLine";
+import { EngravingRules } from "../EngravingRules";
+import { VexflowStafflineNoteCalculator } from "./VexflowStafflineNoteCalculator";
+import { MusicSystem } from "../MusicSystem";
+import { NoteTypeHandler } from "../../VoiceData/NoteType";
+import { VexFlowConverter } from "./VexFlowConverter";
+import { TabNote } from "../../VoiceData/TabNote";
 
 export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
   /** space needed for a dash for lyrics spacing, calculated once */
   private dashSpace: number;
+  public beamsNeedUpdate: boolean = false;
 
-  constructor() {
+  constructor(rules: EngravingRules) {
     super();
+    this.rules = rules;
     MusicSheetCalculator.symbolFactory = new VexFlowGraphicalSymbolFactory();
-    MusicSheetCalculator.TextMeasurer = new VexFlowTextMeasurer();
+    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?.toLowerCase() === "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 if (this.rules.DefaultVexFlowNoteFont?.toLowerCase() === "petaluma") {
+      (Vex.Flow as any).DEFAULT_FONT_STACK = [(Vex.Flow as any).Fonts?.Petaluma, (Vex.Flow as any).Fonts?.Gonville, (Vex.Flow as any).Fonts?.Bravura];
+    }
+    // else keep new vexflow default Bravura (more cursive, bold)
   }
 
   protected clearRecreatedObjects(): void {
     super.clearRecreatedObjects();
+    MusicSheetCalculator.stafflineNoteCalculator = new VexflowStafflineNoteCalculator(this.rules);
     for (const graphicalMeasures of this.graphicalMusicSheet.MeasureList) {
       for (const graphicalMeasure of graphicalMeasures) {
-        (<VexFlowMeasure>graphicalMeasure).clean();
+        (<VexFlowMeasure>graphicalMeasure)?.clean();
       }
     }
   }
 
   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();
@@ -75,8 +99,17 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
         for (const staffEntry of measure.staffEntries) {
           (<VexFlowStaffEntry>staffEntry).calculateXPosition();
         }
+        // const t0: number = performance.now();
+        if (true || this.beamsNeedUpdate) {
+          // finalizeBeams takes a few milliseconds, so we can save some performance here sometimes,
+          // but we'd have to check for every setting change that would affect beam rendering. See #843
+          (measure as VexFlowMeasure).finalizeBeams(); // without this, when zooming a lot (e.g. 250%), beams keep their old, now wrong slope.
+          // totalFinalizeBeamsTime += performance.now() - t0;
+          // console.log("Total calls to finalizeBeams in VexFlowMusicSheetCalculator took " + totalFinalizeBeamsTime + " milliseconds.");
+        }
       }
     }
+    this.beamsNeedUpdate = false;
   }
 
   //protected clearSystemsAndMeasures(): void {
@@ -97,16 +130,23 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
    * @returns the minimum required x width of the source measure (=list of staff measures)
    */
   protected calculateMeasureXLayout(measures: GraphicalMeasure[]): number {
-    // Finalize beams
-    /*for (let measure of measures) {
-     (measure as VexFlowMeasure).finalizeBeams();
-     (measure as VexFlowMeasure).finalizeTuplets();
-     }*/
+    const visibleMeasures: GraphicalMeasure[] = [];
+    for (const measure of measures) {
+      visibleMeasures.push(measure);
+    }
+    measures = visibleMeasures;
+
     // Format the voices
     const allVoices: Vex.Flow.Voice[] = [];
-    const formatter: Vex.Flow.Formatter = new Vex.Flow.Formatter();
+    const formatter: Vex.Flow.Formatter = new Vex.Flow.Formatter({
+      // maxIterations: 2,
+      softmaxFactor: this.rules.SoftmaxFactorVexFlow // this setting is only applied in Vexflow 3.x. also this needs @types/vexflow ^3.0.0
+    });
 
     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) {
@@ -115,44 +155,103 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
           allVoices.push(mvoices[voiceID]);
         }
       }
+
       if (voices.length === 0) {
-        log.info("Found a measure with no voices. Continuing anyway.", mvoices);
+        log.debug("Found a measure with no voices. Continuing anyway.", mvoices);
+        // no need to log this, measures with no voices/notes are fine. see OSMDOptions.fillEmptyMeasuresWithWholeRest
         continue;
       }
       // all voices that belong to one stave are collectively added to create a common context in VexFlow.
       formatter.joinVoices(voices);
     }
 
-    let minStaffEntriesWidth: number = 200;
+    let minStaffEntriesWidth: number = 12; // a typical measure has roughly a length of 3*StaffHeight (3*4 = 12)
     if (allVoices.length > 0) {
-      // FIXME: The following ``+ 5.0'' is temporary: it was added as a workaround for
-      // FIXME: a more relaxed formatting of voices
-      minStaffEntriesWidth = formatter.preCalculateMinTotalWidth(allVoices) / unitInPixels + 5.0;
+      // the voicing space bonus addition makes the voicing more relaxed. With a bonus of 0 the notes are basically completely squeezed together.
+      const staffEntryFactor: number = 0.3;
+
+      minStaffEntriesWidth = formatter.preCalculateMinTotalWidth(allVoices) / unitInPixels
+        * this.rules.VoiceSpacingMultiplierVexflow
+        + this.rules.VoiceSpacingAddendVexflow
+        + measures[0].staffEntries.length * staffEntryFactor;
+        // TODO this could use some fine-tuning. currently using *1.5 + 1 by default, results in decent spacing.
       // firstMeasure.formatVoices = (w: number) => {
       //     formatter.format(allVoices, w);
       // };
       MusicSheetCalculator.setMeasuresMinStaffEntriesWidth(measures, minStaffEntriesWidth);
+
+      const formatVoicesDefault: (w: number, p: VexFlowMeasure) => void = (w, p) => {
+        formatter.formatToStave(allVoices, p.getVFStave());
+      };
+      const formatVoicesAlignRests: (w: number,  p: VexFlowMeasure) => void = (w, p) => {
+        formatter.formatToStave(allVoices, p.getVFStave(), {
+          align_rests: true,
+          context: undefined
+        });
+      };
+
       for (const measure of measures) {
+        // determine whether to align rests
+        if (this.rules.AlignRests === AlignRestOption.Never) {
+          (measure as VexFlowMeasure).formatVoices = formatVoicesDefault;
+        } else if (this.rules.AlignRests === AlignRestOption.Always) {
+          (measure as VexFlowMeasure).formatVoices = formatVoicesAlignRests;
+        } else if (this.rules.AlignRests === AlignRestOption.Auto) {
+          let alignRests: boolean = false;
+          for (const staffEntry of measure.staffEntries) {
+            let collidableVoiceEntries: number = 0;
+            let numberOfRests: number = 0;
+            for (const voiceEntry of staffEntry.graphicalVoiceEntries) {
+              if (!voiceEntry.parentVoiceEntry.IsGrace) {
+                if (voiceEntry && voiceEntry.notes && voiceEntry.notes[0] && voiceEntry.notes[0].sourceNote) {// TODO null chaining, TS 3.7
+                  if (voiceEntry.notes[0].sourceNote.PrintObject) { // only respect collision when not invisible
+                    collidableVoiceEntries++;
+                  }
+                }
+              }
+              if (voiceEntry && voiceEntry.notes && voiceEntry.notes[0] && voiceEntry.notes[0].sourceNote) {// TODO null chaining, TS 3.7
+                if (voiceEntry.notes[0].sourceNote.isRest() && voiceEntry.notes[0].sourceNote.PrintObject) {
+                  numberOfRests++; // only align rests if there is actually a rest (which could collide)
+                }
+              }
+              if (collidableVoiceEntries > 1 && numberOfRests >= 1) {
+                // TODO could add further checks like if any of the already checked voice entries actually collide
+                alignRests = true;
+                break;
+              }
+            }
+            if (alignRests) {
+              break;
+            }
+          }
+
+          // set measure's format function
+          if (alignRests) {
+            (measure as VexFlowMeasure).formatVoices = formatVoicesAlignRests;
+          } else {
+            (measure as VexFlowMeasure).formatVoices = formatVoicesDefault;
+          }
+        }
+
+        // format first measure with minimum width
         if (measure === measures[0]) {
           const vexflowMeasure: VexFlowMeasure = (measure as VexFlowMeasure);
           // prepare format function for voices, will be called later for formatting measure again
-          vexflowMeasure.formatVoices = (w: number) => {
-            formatter.format(allVoices, w);
-            // formatter.format(allVoices, w, {
-            //   align_rests: false, // TODO
-            //   // align_rests = true causes a Vexflow Exception for Mozart - An Chloe
-            //   // align_rests = false still aligns rests with beams according to Vexflow, but doesn't seem to do anything
-            // });
-          };
+          //vexflowMeasure.formatVoices = formatVoicesDefault;
+
           // format now for minimum width, calculateMeasureWidthFromLyrics later
-          vexflowMeasure.formatVoices(minStaffEntriesWidth * unitInPixels);
+          vexflowMeasure.formatVoices(minStaffEntriesWidth * unitInPixels, vexflowMeasure);
         } else {
-          (measure as VexFlowMeasure).formatVoices = undefined;
+          //(measure as VexFlowMeasure).formatVoices = undefined;
+          // TODO why was the formatVoices function disabled for other measures? would now disable the new align rests option.
         }
       }
     }
 
     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();
@@ -180,6 +279,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
@@ -193,18 +295,18 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
           const lyricsEntry: GraphicalLyricEntry = staffEntry.LyricsEntries[j];
           // const lyricsEntryText = lyricsEntry.LyricsEntry.Text; // for easier debugging
           const lyricAlignment: TextAlignmentEnum = lyricsEntry.GraphicalLabel.Label.textAlignment;
-          let minLyricsSpacing: number = EngravingRules.Rules.HorizontalBetweenLyricsDistance;
+          let minLyricsSpacing: number = this.rules.HorizontalBetweenLyricsDistance;
           // for quarter note in Vexflow, where spacing is halfed for each smaller note duration.
 
           let lyricOverlapAllowedIntoNextMeasure: number =
-            EngravingRules.Rules.LyricOverlapAllowedIntoNextMeasure;
+            this.rules.LyricOverlapAllowedIntoNextMeasure;
           // TODO allow more overlap if there are no lyrics in next measure
 
           // spacing for multi-syllable words
           if (lyricsEntry.ParentLyricWord) {
             if (lyricsEntry.LyricsEntry.SyllableIndex > 0) { // syllables after first
               // give a little more spacing for dash between syllables
-              minLyricsSpacing = EngravingRules.Rules.BetweenSyllableMinimumDistance;
+              minLyricsSpacing = this.rules.BetweenSyllableMinimumDistance;
               if (TextAlignment.IsCenterAligned(lyricsEntry.GraphicalLabel.Label.textAlignment)) {
                 minLyricsSpacing += 1.0; // TODO check for previous lyric alignment too. though center is not standard
                 // without this, there's not enough space for dashes between long syllables on eigth notes
@@ -214,7 +316,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
             if (syllables.length > 1) {
               if (lyricsEntry.LyricsEntry.SyllableIndex < syllables.length - 1) {
                 // if a middle syllable of a word, give less measure overlap into next measure, to give room for dash
-                if (this.dashSpace === undefined) {
+                if (this.dashSpace === undefined) { // don't replace undefined check
                   this.dashSpace = 1.5;
                   // better method, doesn't work:
                   // this.dashLength = new GraphicalLabel(new Label("-"), this.rules.LyricsHeight, TextAlignmentEnum.CenterBottom)
@@ -230,7 +332,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
           const staffEntryXPosition: number = (staffEntry as VexFlowStaffEntry).PositionAndShape.RelativePosition.x;
           const lyricsXPosition: number = staffEntryXPosition + lyricsBbox.BorderMarginLeft;
 
-          if (lastLyricEntryDict[j] !== undefined) {
+          if (lastLyricEntryDict[j]) {
             if (lastLyricEntryDict[j].extend) {
               // TODO handle extend of last entry (extend is stored in lyrics entry of preceding syllable)
               // only necessary for center alignment
@@ -302,6 +404,8 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
         }
       }
     }
+    elongationFactorForMeasureWidth = Math.min(elongationFactorForMeasureWidth, this.rules.MaximumLyricsElongationFactor);
+    // TODO check when this is > 2.0. there seems to be an error here where this is unnecessarily > 2 in Beethoven Geliebte.
     return oldMinimumStaffEntriesWidth * elongationFactorForMeasureWidth;
   }
 
@@ -316,6 +420,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
   }
 
   protected graphicalMeasureCreatedCalculations(measure: GraphicalMeasure): void {
+    (measure as VexFlowMeasure).rules = this.rules;
     (measure as VexFlowMeasure).graphicalMeasureCreatedCalculations();
   }
 
@@ -330,7 +435,9 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
    */
   protected layoutVoiceEntry(voiceEntry: VoiceEntry, graphicalNotes: GraphicalNote[], graphicalStaffEntry: GraphicalStaffEntry,
                              hasPitchedNote: boolean): void {
-    return;
+      for (let i: number = 0; i < graphicalNotes.length; i++) {
+        graphicalNotes[i] = MusicSheetCalculator.stafflineNoteCalculator.positionNote(graphicalNotes[i]);
+      }
   }
 
   /**
@@ -343,21 +450,6 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
   }
 
   /**
-   * calculates the y positions of the staff lines within a system and
-   * furthermore the y positions of the systems themselves.
-   */
-  protected calculateSystemYLayout(): void {
-    for (const graphicalMusicPage of this.graphicalMusicSheet.MusicPages) {
-      for (const musicSystem of graphicalMusicPage.MusicSystems) {
-        this.optimizeDistanceBetweenStaffLines(musicSystem);
-      }
-
-      // set y positions of systems using the previous system and a fixed distance.
-      this.calculateMusicSystemsRelativePositions(graphicalMusicPage);
-    }
-  }
-
-  /**
    * Is called at the begin of the method for creating the vertically aligned staff measures belonging to one source measure.
    */
   protected initGraphicalMeasuresCreation(): void {
@@ -370,7 +462,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
    * @param voiceEntry
    * @param graphicalStaffEntry
    */
-  protected layoutArticulationMarks(articulations: ArticulationEnum[], voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
+  protected layoutArticulationMarks(articulations: Articulation[], voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
     // uncomment this when implementing:
     // let vfse: VexFlowStaffEntry = (graphicalStaffEntry as VexFlowStaffEntry);
 
@@ -381,54 +473,100 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
    * Calculate the shape (Bezier curve) for this tie.
    * @param tie
    * @param tieIsAtSystemBreak
+   * @param isTab Whether this tie is for a tab note (guitar tabulature)
    */
-  protected layoutGraphicalTie(tie: GraphicalTie, tieIsAtSystemBreak: boolean): void {
+  protected layoutGraphicalTie(tie: GraphicalTie, tieIsAtSystemBreak: boolean, isTab: boolean): void {
     const startNote: VexFlowGraphicalNote = (tie.StartNote as VexFlowGraphicalNote);
     const endNote: VexFlowGraphicalNote = (tie.EndNote as VexFlowGraphicalNote);
 
-    let vfStartNote: Vex.Flow.StaveNote = undefined;
+    let vfStartNote: Vex.Flow.StemmableNote  = undefined;
     let startNoteIndexInTie: number = 0;
-    if (startNote !== undefined && startNote.vfnote !== undefined && startNote.vfnote.length >= 2) {
+    if (startNote && startNote.vfnote && startNote.vfnote.length >= 2) {
       vfStartNote = startNote.vfnote[0];
       startNoteIndexInTie = startNote.vfnote[1];
     }
 
-    let vfEndNote: Vex.Flow.StaveNote = undefined;
+    let vfEndNote: Vex.Flow.StemmableNote  = undefined;
     let endNoteIndexInTie: number = 0;
-    if (endNote !== undefined && endNote.vfnote !== undefined && endNote.vfnote.length >= 2) {
+    if (endNote && endNote.vfnote && endNote.vfnote.length >= 2) {
       vfEndNote = endNote.vfnote[0];
       endNoteIndexInTie = endNote.vfnote[1];
     }
 
     if (tieIsAtSystemBreak) {
       // split tie into two ties:
-      const vfTie1: Vex.Flow.StaveTie = new Vex.Flow.StaveTie({
-        first_indices: [startNoteIndexInTie],
-        first_note: vfStartNote
-      });
-      const measure1: VexFlowMeasure = (startNote.parentVoiceEntry.parentStaffEntry.parentMeasure as VexFlowMeasure);
-      measure1.vfTies.push(vfTie1);
-
-      const vfTie2: Vex.Flow.StaveTie = new Vex.Flow.StaveTie({
-        last_indices: [endNoteIndexInTie],
-        last_note: vfEndNote
-      });
-      const measure2: VexFlowMeasure = (endNote.parentVoiceEntry.parentStaffEntry.parentMeasure as VexFlowMeasure);
-      measure2.vfTies.push(vfTie2);
+      if (vfStartNote) { // first_note or last_note must be not null in Vexflow
+        const vfTie1: Vex.Flow.StaveTie = new Vex.Flow.StaveTie({
+          first_indices: [startNoteIndexInTie],
+          first_note: vfStartNote
+        });
+        const measure1: VexFlowMeasure = (startNote.parentVoiceEntry.parentStaffEntry.parentMeasure as VexFlowMeasure);
+        measure1.vfTies.push(vfTie1);
+      }
+
+      if (vfEndNote) {
+        const vfTie2: Vex.Flow.StaveTie = new Vex.Flow.StaveTie({
+          last_indices: [endNoteIndexInTie],
+          last_note: vfEndNote
+        });
+        const measure2: VexFlowMeasure = (endNote.parentVoiceEntry.parentStaffEntry.parentMeasure as VexFlowMeasure);
+        measure2.vfTies.push(vfTie2);
+      }
     } else {
       // normal case
-      const vfTie: Vex.Flow.StaveTie = new Vex.Flow.StaveTie({
-        first_indices: [startNoteIndexInTie],
-        first_note: vfStartNote,
-        last_indices: [endNoteIndexInTie],
-        last_note: vfEndNote
-      });
-      const measure: VexFlowMeasure = (endNote.parentVoiceEntry.parentStaffEntry.parentMeasure as VexFlowMeasure);
-      measure.vfTies.push(vfTie);
+      if (vfStartNote || vfEndNote) { // one of these must be not null in Vexflow
+        let vfTie: any;
+        if (isTab) {
+          if (tie.Tie.Type === "S") {
+            //calculate direction
+            const startTieNote: TabNote = <TabNote> tie.StartNote.sourceNote;
+            const endTieNote: TabNote = <TabNote> tie.EndNote.sourceNote;
+            let slideDirection: number = 1;
+            if (startTieNote.FretNumber > endTieNote.FretNumber) {
+              slideDirection = -1;
+            }
+            vfTie = new Vex.Flow.TabSlide(
+              {
+                first_indices: [startNoteIndexInTie],
+                first_note: vfStartNote,
+                last_indices: [endNoteIndexInTie],
+                last_note: vfEndNote,
+              },
+              slideDirection
+            );
+          } else {
+            vfTie = new Vex.Flow.TabTie(
+              {
+                first_indices: [startNoteIndexInTie],
+                first_note: vfStartNote,
+                last_indices: [endNoteIndexInTie],
+                last_note: vfEndNote,
+              },
+              tie.Tie.Type
+            );
+          }
+
+        } else { // not Tab (guitar), normal StaveTie
+          vfTie = new Vex.Flow.StaveTie({
+            first_indices: [startNoteIndexInTie],
+            first_note: vfStartNote,
+            last_indices: [endNoteIndexInTie],
+            last_note: vfEndNote
+          });
+        }
+
+        const measure: VexFlowMeasure = (endNote.parentVoiceEntry.parentStaffEntry.parentMeasure as VexFlowMeasure);
+        measure.vfTies.push(vfTie);
+      }
     }
   }
 
   protected calculateDynamicExpressionsForMultiExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
+    if (measureIndex < this.rules.MinMeasureToDrawIndex || measureIndex > this.rules.MaxMeasureToDrawIndex) {
+      return;
+      // we do already use the min/max in MusicSheetCalculator.calculateDynamicsExpressions,
+      // but this may be necessary for StaffLinkedExpressions, not tested.
+    }
 
     // calculate absolute Timestamp
     const absoluteTimestamp: Fraction = multiExpression.AbsoluteTimestamp;
@@ -440,7 +578,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
       absoluteTimestamp,
       staffIndex,
       staffLine,
-      staffLine.isPartOfMultiStaffInstrument());
+      staffLine?.isPartOfMultiStaffInstrument());
 
     const dynamicStartPosition: PointF2D = startPosInStaffline;
     if (startPosInStaffline.x <= 0) {
@@ -458,11 +596,18 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
       const continuousDynamic: ContinuousDynamicExpression = multiExpression.StartingContinuousDynamic;
       const graphicalContinuousDynamic: VexFlowContinuousDynamicExpression = new VexFlowContinuousDynamicExpression(
         multiExpression.StartingContinuousDynamic,
-        staffLine);
+        staffLine,
+        startMeasure.parentSourceMeasure);
       graphicalContinuousDynamic.StartMeasure = startMeasure;
 
       if (!graphicalContinuousDynamic.IsVerbal && continuousDynamic.EndMultiExpression) {
+        try {
         this.calculateGraphicalContinuousDynamic(graphicalContinuousDynamic, dynamicStartPosition);
+        } catch (e) {
+          // TODO this sometimes fails when the measure range to draw doesn't include all the dynamic's measures, method needs to be adjusted
+          //   see calculateGraphicalContinuousDynamic(), also in MusicSheetCalculator.
+
+        }
       } else if (graphicalContinuousDynamic.IsVerbal) {
         this.calculateGraphicalVerbalContinuousDynamic(graphicalContinuousDynamic, dynamicStartPosition);
       } else {
@@ -472,25 +617,34 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
   }
 
   protected createMetronomeMark(metronomeExpression: InstantaneousTempoExpression): void {
-    const vfStave: Vex.Flow.Stave = (this.graphicalMusicSheet.MeasureList[0][0] as VexFlowMeasure).getVFStave();
+    // note: sometimes MeasureNumber is 0 here, e.g. in Christbaum, maybe because of pickup measure (auftakt)
+    const measureNumber: number = Math.max(metronomeExpression.ParentMultiTempoExpression.SourceMeasureParent.MeasureNumber - 1, 0);
+    const staffNumber: number = Math.max(metronomeExpression.StaffNumber - 1, 0);
+    const firstMetronomeMark: boolean = measureNumber === 0 && staffNumber === 0;
+    const vfStave: Vex.Flow.Stave = (this.graphicalMusicSheet.MeasureList[measureNumber][staffNumber] as VexFlowMeasure).getVFStave();
     //vfStave.addModifier(new Vex.Flow.StaveTempo( // needs Vexflow PR
+    let vexflowDuration: string = "q";
+    if (metronomeExpression.beatUnit) {
+      const duration: Fraction = NoteTypeHandler.getNoteDurationFromType(metronomeExpression.beatUnit);
+      vexflowDuration = VexFlowConverter.duration(duration, false);
+    }
+
     vfStave.setTempo(
       {
           bpm: metronomeExpression.TempoInBpm,
           dots: metronomeExpression.dotted,
-          //duration: metronomeExpression.beatUnit
-          duration: "q"
+          duration: vexflowDuration
       },
-      EngravingRules.Rules.MetronomeMarkYShift * unitInPixels);
+      this.rules.MetronomeMarkYShift * unitInPixels);
        // -50, -30), 0); //needs Vexflow PR
        //.setShiftX(-50);
-
+    const xShift: number = firstMetronomeMark ? this.rules.MetronomeMarkXShift * unitInPixels : 0;
     (<any>vfStave.getModifiers()[vfStave.getModifiers().length - 1]).setShiftX(
-      EngravingRules.Rules.MetronomeMarkXShift * unitInPixels
+      xShift
     );
     // TODO calculate bounding box of metronome mark instead of hacking skyline to fix lyricist collision
     const skyline: number[] = this.graphicalMusicSheet.MeasureList[0][0].ParentStaffLine.SkyLine;
-    skyline[0] = Math.min(skyline[0], -4.5 + EngravingRules.Rules.MetronomeMarkYShift);
+    skyline[0] = Math.min(skyline[0], -4.5 + this.rules.MetronomeMarkYShift);
     // somehow this is called repeatedly in Clementi, so skyline[0] = Math.min instead of -=
   }
 
@@ -506,50 +660,189 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     const octaveShift: OctaveShift = multiExpression.OctaveShiftStart;
 
     const startTimeStamp: Fraction = octaveShift.ParentStartMultiExpression.Timestamp;
-    const endTimeStamp: Fraction = octaveShift.ParentEndMultiExpression.Timestamp;
+    const endTimeStamp: Fraction = octaveShift.ParentEndMultiExpression?.Timestamp;
 
-    const startStaffLine: StaffLine = this.graphicalMusicSheet.MeasureList[measureIndex][staffIndex].ParentStaffLine;
+    const minMeasureToDrawIndex: number = this.rules.MinMeasureToDrawIndex;
+    const maxMeasureToDrawIndex: number = this.rules.MaxMeasureToDrawIndex;
+
+    let startStaffLine: StaffLine = this.graphicalMusicSheet.MeasureList[measureIndex][staffIndex].ParentStaffLine;
+    if (!startStaffLine) { // fix for rendering range set. all of these can probably done cleaner.
+      startStaffLine = this.graphicalMusicSheet.MeasureList[minMeasureToDrawIndex][staffIndex].ParentStaffLine;
+    }
 
     let endMeasure: GraphicalMeasure = undefined;
-    if (octaveShift.ParentEndMultiExpression !== undefined) {
+    if (octaveShift.ParentEndMultiExpression) {
       endMeasure = this.graphicalMusicSheet.getGraphicalMeasureFromSourceMeasureAndIndex(octaveShift.ParentEndMultiExpression.SourceMeasureParent,
                                                                                          staffIndex);
+    } else {
+      endMeasure = this.graphicalMusicSheet.getLastGraphicalMeasureFromIndex(staffIndex, true); // get last rendered measure
+    }
+    if (endMeasure.MeasureNumber > maxMeasureToDrawIndex + 1) { // octaveshift ends in measure not rendered
+      endMeasure = this.graphicalMusicSheet.getLastGraphicalMeasureFromIndex(staffIndex, true);
     }
     let startMeasure: GraphicalMeasure = undefined;
-    if (octaveShift.ParentEndMultiExpression !== undefined) {
+    if (octaveShift.ParentEndMultiExpression) {
       startMeasure = this.graphicalMusicSheet.getGraphicalMeasureFromSourceMeasureAndIndex(octaveShift.ParentStartMultiExpression.SourceMeasureParent,
                                                                                            staffIndex);
+    } else {
+      startMeasure = this.graphicalMusicSheet.MeasureList[minMeasureToDrawIndex][staffIndex]; // first rendered measure
+    }
+    if (startMeasure.MeasureNumber < minMeasureToDrawIndex + 1) { // octaveshift starts before range of measures selected to render
+      startMeasure = this.graphicalMusicSheet.MeasureList[minMeasureToDrawIndex][staffIndex]; // first rendered measure
     }
 
-    if (endMeasure !== undefined) {
+    if (startMeasure.MeasureNumber < minMeasureToDrawIndex + 1 ||
+        startMeasure.MeasureNumber > maxMeasureToDrawIndex + 1 ||
+        endMeasure.MeasureNumber < minMeasureToDrawIndex + 1 ||
+        endMeasure.MeasureNumber > maxMeasureToDrawIndex + 1) {
+      // octave shift completely out of drawing range, don't draw anything
+      return;
+    }
+
+    let endStaffLine: StaffLine = endMeasure.ParentStaffLine;
+    if (!endStaffLine) {
+      endStaffLine = startStaffLine;
+    }
+
+    if (endMeasure && startStaffLine && endStaffLine) {
       // calculate GraphicalOctaveShift and RelativePositions
       const graphicalOctaveShift: VexFlowOctaveShift = new VexFlowOctaveShift(octaveShift, startStaffLine.PositionAndShape);
-      startStaffLine.OctaveShifts.push(graphicalOctaveShift);
-
+      if (!graphicalOctaveShift.startNote) { // fix for rendering range set
+        let startGse: GraphicalStaffEntry;
+        for (const gse of startMeasure.staffEntries) {
+          if (gse) {
+            startGse = gse;
+            break;
+          } // sometimes the first graphical staff entry is undefined, not sure why.
+        }
+        if (!startGse) {
+          return; // couldn't find a start staffentry, don't draw the octave shift
+        }
+        graphicalOctaveShift.setStartNote(startGse);
+        if (!graphicalOctaveShift.startNote) {
+          return; // couldn't find a start note, don't draw the octave shift
+        }
+      }
+      if (!graphicalOctaveShift.endNote) { // fix for rendering range set
+        let endGse: GraphicalStaffEntry;
+        for (let i: number = endMeasure.staffEntries.length - 1; i >= 0; i++) {
+          // search backwards from end of measure
+          if (endMeasure.staffEntries[i]) {
+            endGse = endMeasure.staffEntries[i];
+            break;
+          }
+        }
+        graphicalOctaveShift.setEndNote(endGse);
+        if (!graphicalOctaveShift.endNote) {
+          return;
+        }
+      }
       // calculate RelativePosition and Dashes
-      const startStaffEntry: GraphicalStaffEntry = startMeasure.findGraphicalStaffEntryFromTimestamp(startTimeStamp);
-      const endStaffEntry: GraphicalStaffEntry = endMeasure.findGraphicalStaffEntryFromTimestamp(endTimeStamp);
-
+      let startStaffEntry: GraphicalStaffEntry = startMeasure.findGraphicalStaffEntryFromTimestamp(startTimeStamp);
+      if (!startStaffEntry) { // fix for rendering range set
+        startStaffEntry = startMeasure.staffEntries[0];
+      }
+      let endStaffEntry: GraphicalStaffEntry = endMeasure.findGraphicalStaffEntryFromTimestamp(endTimeStamp);
+      if (!endStaffEntry) { // fix for rendering range set
+        endStaffEntry = endMeasure.staffEntries[endMeasure.staffEntries.length - 1];
+      }
       graphicalOctaveShift.setStartNote(startStaffEntry);
 
-      if (endMeasure.ParentStaffLine !== startMeasure.ParentStaffLine) {
+      if (endStaffLine !== startStaffLine) {
         graphicalOctaveShift.endsOnDifferentStaffLine = true;
-        const lastMeasure: GraphicalMeasure = startMeasure.ParentStaffLine.Measures[startMeasure.ParentStaffLine.Measures.length - 1];
-        const lastNote: GraphicalStaffEntry = lastMeasure.staffEntries[lastMeasure.staffEntries.length - 1];
-        graphicalOctaveShift.setEndNote(lastNote);
-
-        // Now finish the shift on the next line
-        const remainingOctaveShift: VexFlowOctaveShift = new VexFlowOctaveShift(octaveShift, endMeasure.PositionAndShape);
-        endMeasure.ParentStaffLine.OctaveShifts.push(remainingOctaveShift);
-        const firstMeasure: GraphicalMeasure = endMeasure.ParentStaffLine.Measures[0];
-        const firstNote: GraphicalStaffEntry = firstMeasure.staffEntries[0];
-        remainingOctaveShift.setStartNote(firstNote);
-        remainingOctaveShift.setEndNote(endStaffEntry);
+        let lastMeasureOfFirstShift: GraphicalMeasure = startStaffLine.Measures[startStaffLine.Measures.length - 1];
+        if (lastMeasureOfFirstShift === undefined) { // TODO handle this case correctly (when drawUpToMeasureNumber etc set)
+          lastMeasureOfFirstShift = endMeasure;
+        }
+        const lastNoteOfFirstShift: GraphicalStaffEntry = lastMeasureOfFirstShift.staffEntries[lastMeasureOfFirstShift.staffEntries.length - 1];
+        graphicalOctaveShift.setEndNote(lastNoteOfFirstShift);
+
+        const systemsInBetweenCount: number = endStaffLine.ParentMusicSystem.Id - startStaffLine.ParentMusicSystem.Id;
+        if (systemsInBetweenCount > 0) {
+          //Loop through the stafflines in between to the end
+          for (let i: number = startStaffLine.ParentMusicSystem.Id; i < endStaffLine.ParentMusicSystem.Id; i++) {
+            const idx: number = i + 1;
+            const nextShiftMusicSystem: MusicSystem = this.musicSystems[idx];
+            const nextShiftStaffline: StaffLine = nextShiftMusicSystem.StaffLines[staffIndex];
+            const nextShiftFirstMeasure: GraphicalMeasure = nextShiftStaffline.Measures[0];
+            // Shift starts on the first measure
+            const nextOctaveShift: VexFlowOctaveShift = new VexFlowOctaveShift(octaveShift, nextShiftFirstMeasure.PositionAndShape);
+
+            if (i < systemsInBetweenCount) {
+              nextOctaveShift.endsOnDifferentStaffLine = true;
+            }
+
+            let nextShiftLastMeasure: GraphicalMeasure = nextShiftStaffline.Measures[nextShiftStaffline.Measures.length - 1];
+            const firstNote: GraphicalStaffEntry = nextShiftFirstMeasure.staffEntries[0];
+            let lastNote: GraphicalStaffEntry = nextShiftLastMeasure.staffEntries[nextShiftLastMeasure.staffEntries.length - 1];
+
+            //If the is the ending staffline, this endMeasure is the end of the shift
+            if (endMeasure.ParentStaffLine === nextShiftStaffline) {
+              nextShiftLastMeasure = endMeasure;
+              lastNote = endStaffEntry;
+            }
+
+            nextOctaveShift.setStartNote(firstNote);
+            nextOctaveShift.setEndNote(lastNote);
+            nextShiftStaffline.OctaveShifts.push(nextOctaveShift);
+            this.calculateOctaveShiftSkyBottomLine(firstNote, lastNote, nextOctaveShift, nextShiftStaffline);
+          }
+        }
+
+        this.calculateOctaveShiftSkyBottomLine(startStaffEntry, lastNoteOfFirstShift, graphicalOctaveShift, startStaffLine);
       } else {
         graphicalOctaveShift.setEndNote(endStaffEntry);
+        this.calculateOctaveShiftSkyBottomLine(startStaffEntry, endStaffEntry, graphicalOctaveShift, startStaffLine);
+      }
+      startStaffLine.OctaveShifts.push(graphicalOctaveShift);
+    } else {
+      log.warn("End measure or staffLines for octave shift are undefined! This should not happen!");
+    }
+  }
+
+  private calculateOctaveShiftSkyBottomLine(startStaffEntry: GraphicalStaffEntry, endStaffEntry: GraphicalStaffEntry,
+                                            vfOctaveShift: VexFlowOctaveShift, parentStaffline: StaffLine): void {
+
+    let startXOffset: number = startStaffEntry.PositionAndShape.Size.width;
+    let endXOffset: number = endStaffEntry.PositionAndShape.Size.width;
+
+    //Vexflow renders differently with rests
+    if (startStaffEntry.hasOnlyRests()) {
+      startXOffset = -startXOffset;
+    } else {
+      startXOffset /= 2;
+    }
+
+    if (!endStaffEntry.hasOnlyRests()) {
+      endXOffset /= 2;
+    } else {
+      endXOffset *= 2;
+    }
+
+    if (startStaffEntry === endStaffEntry) {
+      endXOffset *= 2;
+    }
+    const startX: number = startStaffEntry.PositionAndShape.AbsolutePosition.x - startXOffset;
+    const stopX: number = endStaffEntry.PositionAndShape.AbsolutePosition.x + endXOffset;
+    vfOctaveShift.PositionAndShape.Size.width = startX - stopX;
+    const textBracket: Vex.Flow.TextBracket = vfOctaveShift.getTextBracket();
+    const fontSize: number = (textBracket as any).font.size / 10;
+
+    if ((<any>textBracket).position === Vex.Flow.TextBracket.Positions.TOP) {
+      const headroom: number = Math.ceil(parentStaffline.SkyBottomLineCalculator.getSkyLineMinInRange(startX, stopX));
+      if (headroom === Infinity) { // will cause Vexflow error
+        return;
       }
+      (textBracket.start.getStave().options as any).top_text_position = Math.abs(headroom);
+      parentStaffline.SkyBottomLineCalculator.updateSkyLineInRange(startX, stopX, headroom - fontSize * 2);
     } else {
-      log.warn("End measure for octave shift is undefined! This should not happen!");
+      const footroom: number = parentStaffline.SkyBottomLineCalculator.getBottomLineMaxInRange(startX, stopX);
+      if (footroom === Infinity) { // will cause Vexflow error
+        return;
+      }
+      (textBracket.start.getStave().options as any).bottom_text_position = footroom;
+      //Vexflow positions top vs. bottom text in a slightly inconsistent way it seems
+      parentStaffline.SkyBottomLineCalculator.updateBottomLineInRange(startX, stopX, footroom + fontSize * 1.5);
     }
   }
 
@@ -564,7 +857,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     const measures: VexFlowMeasure[] = <VexFlowMeasure[]>this.graphicalMusicSheet.MeasureList[measureIndex];
     for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
       const graphicalMeasure: VexFlowMeasure = measures[idx];
-      if (graphicalMeasure.ParentStaffLine !== undefined && graphicalMeasure.ParentStaff.ParentInstrument.Visible) {
+      if (graphicalMeasure && graphicalMeasure.ParentStaffLine && graphicalMeasure.ParentStaff.ParentInstrument.Visible) {
         uppermostMeasure = <VexFlowMeasure>graphicalMeasure;
         break;
       }
@@ -572,15 +865,31 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     // ToDo: feature/Repetitions
     // now create corresponding graphical symbol or Text in VexFlow:
     // use top measure and staffline for positioning.
-    if (uppermostMeasure !== undefined) {
+    if (uppermostMeasure) {
       uppermostMeasure.addWordRepetition(repetitionInstruction);
     }
   }
 
-  protected calculateMoodAndUnknownExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
-    return;
+  /**
+   * Re-adjust the x positioning of expressions. Update the skyline afterwards
+   */
+  protected calculateExpressionAlignements(): void {
+    for (const musicSystem of this.musicSystems) {
+      for (const staffLine of musicSystem.StaffLines) {
+        try {
+          (<VexFlowStaffLine>staffLine).AlignmentManager.alignDynamicExpressions();
+          staffLine.AbstractExpressions.forEach(ae => {
+            ae.updateSkyBottomLine();
+          });
+        } catch (e) {
+          // TODO still necessary when calculation of expression fails, see calculateDynamicExpressionsForMultiExpression()
+          //   see calculateGraphicalContinuousDynamic(), also in MusicSheetCalculator.
+        }
+      }
+    }
   }
 
+
   /**
    * Check if the tied graphical note belongs to any beams or tuplets and react accordingly.
    * @param tiedGraphicalNote
@@ -621,7 +930,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
       const graphicalLabel: GraphicalLabel = graphicalLyricEntry.GraphicalLabel;
       graphicalLabel.setLabelPositionAndShapeBorders();
 
-      if (lyricsEntry.Word !== undefined) {
+      if (lyricsEntry.Word) {
         const lyricsEntryIndex: number = lyricsEntry.Word.Syllables.indexOf(lyricsEntry);
         let index: number = lyricWords.indexOf(lyricsEntry.Word);
         if (index === -1) {
@@ -660,7 +969,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
    * @param voiceEntry
    * @param graphicalStaffEntry
    */
-  protected handleVoiceEntryArticulations(articulations: ArticulationEnum[],
+  protected handleVoiceEntryArticulations(articulations: Articulation[],
                                           voiceEntry: VoiceEntry, staffEntry: GraphicalStaffEntry): void {
     // uncomment this when implementing:
     // let vfse: VexFlowStaffEntry = (graphicalStaffEntry as VexFlowStaffEntry);
@@ -732,15 +1041,14 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     }
     */
 
-    for (const gmPage of this.graphicalMusicSheet.MusicPages) {
-      for (const musicSystem of gmPage.MusicSystems) {
+    for (const musicSystem of this.musicSystems) {
         for (const staffLine of musicSystem.StaffLines) {
           // if a graphical slur reaches out of the last musicsystem, we have to create another graphical slur reaching into this musicsystem
           // (one slur needs 2 graphical slurs)
           const openGraphicalSlurs: GraphicalSlur[] = openSlursDict[staffLine.ParentStaff.idInMusicSheet];
           for (let slurIndex: number = 0; slurIndex < openGraphicalSlurs.length; slurIndex++) {
             const oldGSlur: GraphicalSlur = openGraphicalSlurs[slurIndex];
-            const newGSlur: GraphicalSlur = new GraphicalSlur(oldGSlur.slur); //Graphicalslur.createFromSlur(oldSlur);
+            const newGSlur: GraphicalSlur = new GraphicalSlur(oldGSlur.slur, this.rules); //Graphicalslur.createFromSlur(oldSlur);
             staffLine.addSlurToStaffline(newGSlur); // every VFSlur is added to the array in the VFStaffline!
             openGraphicalSlurs[slurIndex] = newGSlur;
           }
@@ -760,45 +1068,17 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
           // add reference of slur array to the VexFlowStaffline class
           for (const graphicalMeasure of staffLine.Measures) {
             for (const graphicalStaffEntry of graphicalMeasure.staffEntries) {
-              // for (var idx5: number = 0, len5 = graphicalStaffEntry.GraceStaffEntriesBefore.Count; idx5 < len5; ++idx5) {
-              //     var graceStaffEntry: GraphicalStaffEntry = graphicalStaffEntry.GraceStaffEntriesBefore[idx5];
-              //     if (graceStaffEntry.Notes[0][0].SourceNote.NoteSlurs.Count > 0) {
-              //         var graceNote: Note = graceStaffEntry.Notes[0][0].SourceNote;
-              //         graceStaffEntry.RelInMeasureTimestamp = Fraction.createFromFraction(graphicalStaffEntry.RelInMeasureTimestamp);
-              //         for (var idx6: number = 0, len6 = graceNote.NoteSlurs.Count; idx6 < len6; ++idx6) {
-              //             var graceNoteSlur: Slur = graceNote.NoteSlurs[idx6];
-              //             if (graceNoteSlur.StartNote == graceNote) {
-              //                 var vfSlur: VexFlowSlur = new VexFlowSlur(graceNoteSlur);
-              //                 vfSlur.GraceStart = true;
-              //                 staffLine.GraphicalSlurs.Add(vfSlur);
-              //                 openGraphicalSlurs[i].Add(vfSlur);
-              //                 for (var j: number = graphicalStaffEntry.GraceStaffEntriesBefore.IndexOf(graceStaffEntry);
-              //                     j < graphicalStaffEntry.GraceStaffEntriesBefore.Count; j++)
-              //                        vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry.GraceStaffEntriesBefore[j]);
-              //             }
-              //             if (graceNote == graceNoteSlur.EndNote) {
-              //                 var vfSlur: VexFlowSlur = findGraphicalSlurFromSlur(openGraphicalSlurs[i], graceNoteSlur);
-              //                 if (vfSlur != null) {
-              //                     vfSlur.GraceEnd = true;
-              //                     openGraphicalSlurs[i].Remove(vfSlur);
-              //                     for (var j: number = 0; j <= graphicalStaffEntry.GraceStaffEntriesBefore.IndexOf(graceStaffEntry); j++)
-              //                         vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry.GraceStaffEntriesBefore[j]);
-              //                 }
-              //             }
-              //         }
-              //     }
-              // }
               // loop over "normal" notes (= no gracenotes)
               for (const graphicalVoiceEntry of graphicalStaffEntry.graphicalVoiceEntries) {
                 for (const graphicalNote of graphicalVoiceEntry.notes) {
                   for (const slur of graphicalNote.sourceNote.NoteSlurs) {
                     // extra check for some MusicSheets that have openSlurs (because only the first Page is available -> Recordare files)
-                    if (slur.EndNote === undefined || slur.StartNote === undefined) {
+                    if (!slur.EndNote || !slur.StartNote) {
                       continue;
                     }
                     // add new VexFlowSlur to List
                     if (slur.StartNote === graphicalNote.sourceNote) {
-                      if (graphicalNote.sourceNote.NoteTie !== undefined) {
+                      if (graphicalNote.sourceNote.NoteTie) {
                         if (graphicalNote.parentVoiceEntry.parentStaffEntry.getAbsoluteTimestamp() !==
                           graphicalNote.sourceNote.NoteTie.StartNote.getAbsoluteTimestamp()) {
                           break;
@@ -806,7 +1086,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
                       }
 
                       // Add a Graphical Slur to the staffline, if the recent note is the Startnote of a slur
-                      const gSlur: GraphicalSlur = new GraphicalSlur(slur);
+                      const gSlur: GraphicalSlur = new GraphicalSlur(slur, this.rules);
                       openGraphicalSlurs.push(gSlur);
                       staffLine.addSlurToStaffline(gSlur);
 
@@ -844,35 +1124,6 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
                   }
                 }
               }
-              // for (var idx5: number = 0, len5 = graphicalStaffEntry.GraceStaffEntriesAfter.Count; idx5 < len5; ++idx5) {
-              //     var graceStaffEntry: GraphicalStaffEntry = graphicalStaffEntry.GraceStaffEntriesAfter[idx5];
-              //     if (graceStaffEntry.Notes[0][0].SourceNote.NoteSlurs.Count > 0) {
-              //         var graceNote: Note = graceStaffEntry.Notes[0][0].SourceNote;
-              //         graceStaffEntry.RelInMeasureTimestamp = Fraction.createFromFraction(graphicalStaffEntry.RelInMeasureTimestamp);
-              //         for (var idx6: number = 0, len6 = graceNote.NoteSlurs.Count; idx6 < len6; ++idx6) {
-              //             var graceNoteSlur: Slur = graceNote.NoteSlurs[idx6];
-              //             if (graceNoteSlur.StartNote == graceNote) {
-              //                 var vfSlur: VexFlowSlur = new VexFlowSlur(graceNoteSlur);
-              //                 vfSlur.GraceStart = true;
-              //                 staffLine.GraphicalSlurs.Add(vfSlur);
-              //                 openGraphicalSlurs[i].Add(vfSlur);
-              //                 for (var j: number = graphicalStaffEntry.GraceStaffEntriesAfter.IndexOf(graceStaffEntry);
-              //                      j < graphicalStaffEntry.GraceStaffEntriesAfter.Count; j++)
-              //                        vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry.GraceStaffEntriesAfter[j]);
-              //             }
-              //             if (graceNote == graceNoteSlur.EndNote) {
-              //                 var vfSlur: VexFlowSlur = findGraphicalSlurFromSlur(openGraphicalSlurs[i], graceNoteSlur);
-              //                 if (vfSlur != null) {
-              //                     vfSlur.GraceEnd = true;
-              //                     openGraphicalSlurs[i].Remove(vfSlur);
-              //                     vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry);
-              //                     for (var j: number = 0; j <= graphicalStaffEntry.GraceStaffEntriesAfter.IndexOf(graceStaffEntry); j++)
-              //                         vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry.GraceStaffEntriesAfter[j]);
-              //                 }
-              //             }
-              //         }
-              //     }
-              // }
 
               //add the present Staffentry to all open slurs that don't contain this Staffentry already
               for (const gSlur of openGraphicalSlurs) {
@@ -887,11 +1138,9 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
         // Attach vfSlur array to the vfStaffline to be drawn
         //vfStaffLine.SlursInVFStaffLine = vfSlurs;
       } // loop over MusicSystems
-    } // loop over MusicPages
 
     // order slurs that were saved to the Staffline
-    for (const graphicalMusicPage of this.graphicalMusicSheet.MusicPages) {
-        for (const musicSystem of graphicalMusicPage.MusicSystems) {
+    for (const musicSystem of this.musicSystems) {
           for (const staffLine of musicSystem.StaffLines) {
             // Sort all gSlurs in the staffline using the Compare function in class GraphicalSlurSorter
             const sortedGSlurs: GraphicalSlur[] = staffLine.GraphicalSlurs.sort(GraphicalSlur.Compare);
@@ -906,4 +1155,3 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
         }
       }
   }
-}

+ 137 - 50
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts

@@ -1,4 +1,4 @@
-import Vex = require("vexflow");
+import Vex from "vexflow";
 import { MusicSheetDrawer } from "../MusicSheetDrawer";
 import { RectangleF2D } from "../../../Common/DataObjects/RectangleF2D";
 import { VexFlowMeasure } from "./VexFlowMeasure";
@@ -17,15 +17,17 @@ import { VexFlowInstrumentBrace } from "./VexFlowInstrumentBrace";
 import { GraphicalLyricEntry } from "../GraphicalLyricEntry";
 import { VexFlowStaffLine } from "./VexFlowStaffLine";
 import { StaffLine } from "../StaffLine";
-import { EngravingRules } from "../EngravingRules";
 import { GraphicalSlur } from "../GraphicalSlur";
 import { PlacementEnum } from "../../VoiceData/Expressions/AbstractExpression";
 import { GraphicalInstantaneousTempoExpression } from "../GraphicalInstantaneousTempoExpression";
 import { GraphicalInstantaneousDynamicExpression } from "../GraphicalInstantaneousDynamicExpression";
-import log = require("loglevel");
+import log from "loglevel";
 import { GraphicalContinuousDynamicExpression } from "../GraphicalContinuousDynamicExpression";
 import { VexFlowContinuousDynamicExpression } from "./VexFlowContinuousDynamicExpression";
 import { DrawingParameters } from "../DrawingParameters";
+import { GraphicalMusicPage } from "../GraphicalMusicPage";
+import { GraphicalMusicSheet } from "../GraphicalMusicSheet";
+import { GraphicalUnknownExpression } from "../GraphicalUnknownExpression";
 
 /**
  * This is a global constant which denotes the height in pixels of the space between two lines of the stave
@@ -36,39 +38,67 @@ export const unitInPixels: number = 10;
 
 export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
     private backend: VexFlowBackend;
+    private backends: VexFlowBackend[] = [];
     private zoom: number = 1.0;
+    private pageIdx: number = 0; // this is a bad solution, should use MusicPage.PageNumber instead.
 
-    constructor(element: HTMLElement,
-                backend: VexFlowBackend,
-                drawingParameters: DrawingParameters = new DrawingParameters()) {
-        super(new VexFlowTextMeasurer(), drawingParameters);
-        this.backend = backend;
+    constructor(drawingParameters: DrawingParameters = new DrawingParameters()) {
+        super(new VexFlowTextMeasurer(drawingParameters.Rules), drawingParameters);
     }
 
-    public clear(): void {
-        this.backend.clear();
+    public get Backends(): VexFlowBackend[] {
+        return this.backends;
     }
 
-    /**
-     * Zoom the rendering areas
-     * @param k is the zoom factor
-     */
-    public scale(k: number): void {
-        this.zoom = k;
-        this.backend.scale(this.zoom);
+    public drawSheet(graphicalMusicSheet: GraphicalMusicSheet): void {
+        // 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).
+
+        // sizing defaults in Vexflow
+        (Vex.Flow as any).STAVE_LINE_THICKNESS = this.rules.StaffLineWidth * unitInPixels;
+        (Vex.Flow as any).STEM_WIDTH = this.rules.StemWidth * unitInPixels;
+        // sets scale/size of notes/rest notes:
+        (Vex.Flow as any).DEFAULT_NOTATION_FONT_SCALE = this.rules.VexFlowDefaultNotationFontScale; // default 39
+        (Vex.Flow as any).DEFAULT_TAB_FONT_SCALE = this.rules.VexFlowDefaultTabFontScale; // default 39 // TODO doesn't seem to do anything
+
+        this.pageIdx = 0;
+        for (const graphicalMusicPage of graphicalMusicSheet.MusicPages) {
+            if (graphicalMusicPage.PageNumber > this.rules.MaxPageToDrawNumber) {
+                break;
+            }
+            const backend: VexFlowBackend = this.backends[this.pageIdx];
+            backend.graphicalMusicPage = graphicalMusicPage;
+            backend.scale(this.zoom);
+            //backend.resize(graphicalMusicSheet.ParentMusicSheet.pageWidth * unitInPixels * this.zoom,
+            //               EngravingRules.Rules.PageHeight * unitInPixels * this.zoom);
+            this.pageIdx += 1;
+        }
+
+        this.pageIdx = 0;
+        this.backend = this.backends[0];
+        super.drawSheet(graphicalMusicSheet);
     }
 
-    /**
-     * Resize the rendering areas
-     * @param x
-     * @param y
-     */
-    public resize(x: number, y: number): void {
-        this.backend.resize(x, y);
+    protected drawPage(page: GraphicalMusicPage): void {
+        if (!page) {
+            return;
+        }
+        this.backend = this.backends[page.PageNumber - 1]; // TODO we may need to set this in a couple of other places. this.pageIdx is a bad solution
+        super.drawPage(page);
+        this.pageIdx += 1;
+        this.backend = this.backends[this.pageIdx];
+    }
+
+    public clear(): void {
+        for (const backend of this.backends) {
+            backend.clear();
+        }
     }
 
-    public translate(x: number, y: number): void {
-        this.backend.translate(x, y);
+    public setZoom(zoom: number): void {
+        this.zoom = zoom;
     }
 
     /**
@@ -83,7 +113,9 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
     protected drawStaffLine(staffLine: StaffLine): void {
         super.drawStaffLine(staffLine);
         const absolutePos: PointF2D = staffLine.PositionAndShape.AbsolutePosition;
-        this.drawSlurs(staffLine as VexFlowStaffLine, absolutePos);
+        if (this.rules.RenderSlurs) {
+            this.drawSlurs(staffLine as VexFlowStaffLine, absolutePos);
+        }
     }
 
     private drawSlurs(vfstaffLine: VexFlowStaffLine, absolutePos: PointF2D): void {
@@ -136,7 +168,12 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
             measure.PositionAndShape.AbsolutePosition.x * unitInPixels,
             measure.PositionAndShape.AbsolutePosition.y * unitInPixels
         );
-        measure.draw(this.backend.getContext());
+        try {
+            measure.draw(this.backend.getContext());
+            // Vexflow errors can happen here. If we don't catch errors, rendering will stop after this measure.
+        } catch (ex) {
+            log.warn("VexFlowMusicSheetDrawer.drawMeasure", ex);
+        }
 
         // Draw the StaffEntries
         for (const staffEntry of measure.staffEntries) {
@@ -153,12 +190,38 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
     //     ctx.fillStyle = oldStyle;
     // }
 
-    public drawLine(start: PointF2D, stop: PointF2D, color: string = "#FF0000FF", lineWidth: number = 0.2): void {
+    /** Draws a line in the current backend. Only usable while pages are drawn sequentially, because backend reference is updated in that process.
+     *  To add your own lines after rendering, use DrawOverlayLine.
+     */
+    protected drawLine(start: PointF2D, stop: PointF2D, color: string = "#FF0000FF", lineWidth: number = 0.2): void {
+        // TODO maybe the backend should be given as an argument here as well, otherwise this can't be used after rendering of multiple pages is done.
         start = this.applyScreenTransformation(start);
         stop = this.applyScreenTransformation(stop);
+        /*if (!this.backend) {
+            this.backend = this.backends[0];
+        }*/
         this.backend.renderLine(start, stop, color, lineWidth * unitInPixels);
     }
 
+    /** Lets a user/developer draw an overlay line on the score. Use this instead of drawLine, which is for OSMD internally only.
+     *  The MusicPage has to be specified, because each page and Vexflow backend has its own relative coordinates.
+     *  (the AbsolutePosition of a GraphicalNote is relative to its backend)
+     *  To get a MusicPage, use GraphicalNote.ParentMusicPage.
+     */
+    public DrawOverlayLine(start: PointF2D, stop: PointF2D, musicPage: GraphicalMusicPage,
+                           color: string = "#FF0000FF", lineWidth: number = 0.2): void {
+        if (!musicPage.PageNumber || musicPage.PageNumber > this.backends.length || musicPage.PageNumber < 1) {
+            console.log("VexFlowMusicSheetDrawer.drawOverlayLine: invalid page number / music page number doesn't correspond to an existing backend.");
+            return;
+        }
+        const musicPageIndex: number = musicPage.PageNumber - 1;
+        const backendToUse: VexFlowBackend = this.backends[musicPageIndex];
+
+        start = this.applyScreenTransformation(start);
+        stop = this.applyScreenTransformation(stop);
+        backendToUse.renderLine(start, stop, color, lineWidth * unitInPixels);
+    }
+
     protected drawSkyLine(staffline: StaffLine): void {
         const startPosition: PointF2D = staffline.PositionAndShape.AbsolutePosition;
         const width: number = staffline.PositionAndShape.Size.width;
@@ -183,7 +246,8 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
     private drawSampledLine(line: number[], startPosition: PointF2D, width: number, color: string = "#FF0000FF"): void {
         const indices: number[] = [];
         let currentValue: number = 0;
-
+        //Loops through bottom line, grabs all indices that don't equal the previously grabbed index
+        //Starting with 0 (gets index of all line changes)
         for (let i: number = 0; i < line.length; i++) {
             if (line[i] !== currentValue) {
                 indices.push(i);
@@ -193,7 +257,7 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
 
         const absolute: PointF2D = startPosition;
         if (indices.length > 0) {
-            const samplingUnit: number = EngravingRules.Rules.SamplingUnit;
+            const samplingUnit: number = this.rules.SamplingUnit;
 
             let horizontalStart: PointF2D = new PointF2D(absolute.x, absolute.y);
             let horizontalEnd: PointF2D = new PointF2D(indices[0] / samplingUnit + absolute.x, absolute.y);
@@ -238,12 +302,16 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
 
 
     private drawStaffEntry(staffEntry: GraphicalStaffEntry): void {
-        // Draw ChordSymbol
-        if (staffEntry.graphicalChordContainer !== undefined) {
-            this.drawLabel(staffEntry.graphicalChordContainer.GetGraphicalLabel, <number>GraphicalLayers.Notes);
+        // Draw ChordSymbols
+        if (staffEntry.graphicalChordContainers !== undefined && staffEntry.graphicalChordContainers.length > 0) {
+            for (const graphicalChordContainer of staffEntry.graphicalChordContainers) {
+                this.drawLabel(graphicalChordContainer.GetGraphicalLabel, <number>GraphicalLayers.Notes);
+            }
         }
-        if (staffEntry.LyricsEntries.length > 0) {
-            this.drawLyrics(staffEntry.LyricsEntries, <number>GraphicalLayers.Notes);
+        if (this.rules.RenderLyrics) {
+            if (staffEntry.LyricsEntries.length > 0) {
+                this.drawLyrics(staffEntry.LyricsEntries, <number>GraphicalLayers.Notes);
+            }
         }
     }
 
@@ -271,8 +339,9 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
     protected drawOctaveShifts(staffLine: StaffLine): void {
         for (const graphicalOctaveShift of staffLine.OctaveShifts) {
             if (graphicalOctaveShift) {
-                const ctx: Vex.Flow.RenderContext = this.backend.getContext();
-                const textBracket: Vex.Flow.TextBracket = (graphicalOctaveShift as VexFlowOctaveShift).getTextBracket();
+                const vexFlowOctaveShift: VexFlowOctaveShift = graphicalOctaveShift as VexFlowOctaveShift;
+                const ctx: Vex.IRenderContext = this.backend.getContext();
+                const textBracket: Vex.Flow.TextBracket = vexFlowOctaveShift.getTextBracket();
                 textBracket.setContext(ctx);
                 textBracket.draw();
             }
@@ -297,13 +366,10 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
                 // // Draw Mood
                 // } else if (abstractGraphicalExpression instanceof GraphicalMoodExpression) {
                 //     GraphicalMoodExpression; graphicalMood = (GraphicalMoodExpression); abstractGraphicalExpression;
-                //     drawLabel(graphicalMood.GetGraphicalLabel, (int)GraphicalLayers.Notes);
-                // // Draw Unknown
-                // } else if (abstractGraphicalExpression instanceof GraphicalUnknownExpression) {
-                //     GraphicalUnknownExpression; graphicalUnknown =
-                //         (GraphicalUnknownExpression); abstractGraphicalExpression;
-                //     drawLabel(graphicalUnknown.GetGraphicalLabel, (int)GraphicalLayers.Notes);
-                // }
+                //     drawLabel(graphicalMood.GetGraphicalLabel, <number>GraphicalLayers.Notes);
+            // Draw Unknown
+            } else if (abstractGraphicalExpression instanceof GraphicalUnknownExpression) {
+                this.drawLabel(abstractGraphicalExpression.Label, <number>GraphicalLayers.Notes);
             } else {
                 log.warn("Unkown type of expression!");
             }
@@ -338,17 +404,38 @@ 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 { fontStyle, font, text } = graphicalLabel.Label;
+        const { font } = graphicalLabel.Label;
         let color: string;
-        if (EngravingRules.Rules.ColoringEnabled) {
+        if (this.rules.ColoringEnabled) {
             color = graphicalLabel.Label.colorDefault;
             if (!color) {
-                color = EngravingRules.Rules.DefaultColorLabel;
+                color = this.rules.DefaultColorLabel;
+            }
+        }
+        let { fontStyle, fontFamily } = graphicalLabel.Label;
+        if (!fontStyle) {
+            fontStyle = this.rules.DefaultFontStyle;
+        }
+        if (!fontFamily) {
+            fontFamily = this.rules.DefaultFontFamily;
+        }
+
+        for (let i: number = 0; i < graphicalLabel.TextLines?.length; i++) {
+            const currLine: {text: string, xOffset: number, width: number} = graphicalLabel.TextLines[i];
+            const xOffsetInPixel: number = this.calculatePixelDistance(currLine.xOffset);
+            const linePosition: PointF2D = new PointF2D(screenPosition.x + xOffsetInPixel, screenPosition.y);
+            this.backend.renderText(height, fontStyle, font, currLine.text, fontHeightInPixel, linePosition, color, graphicalLabel.Label.fontFamily);
+            screenPosition.y = screenPosition.y + fontHeightInPixel;
+            if (graphicalLabel.TextLines.length > 1) {
+             screenPosition.y += this.rules.SpacingBetweenTextLines;
             }
         }
-        this.backend.renderText(height, fontStyle, font, text, heightInPixel, screenPosition, color);
+        // font currently unused, replaced by fontFamily
     }
 
     /**

+ 15 - 10
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSystem.ts

@@ -1,5 +1,4 @@
 import {MusicSystem} from "../MusicSystem";
-import {GraphicalMusicPage} from "../GraphicalMusicPage";
 import {SystemLinesEnum} from "../SystemLinesEnum";
 import {SystemLinePosition} from "../SystemLinePosition";
 import {GraphicalMeasure} from "../GraphicalMeasure";
@@ -14,9 +13,9 @@ import { VexFlowInstrumentBrace } from "./VexFlowInstrumentBrace";
 import { SkyBottomLineCalculator } from "../SkyBottomLineCalculator";
 
 export class VexFlowMusicSystem extends MusicSystem {
-    constructor(parent: GraphicalMusicPage, id: number) {
-        super(parent, id);
-
+    constructor(id: number, rules: EngravingRules) {
+        super(id);
+        this.rules = rules;
     }
 
     public calculateBorders(rules: EngravingRules): void {
@@ -56,14 +55,20 @@ export class VexFlowMusicSystem extends MusicSystem {
      */
     protected createSystemLine(xPosition: number, lineWidth: number, lineType: SystemLinesEnum, linePosition: SystemLinePosition,
                                musicSystem: MusicSystem, topMeasure: GraphicalMeasure, bottomMeasure: GraphicalMeasure = undefined): SystemLine {
-        const vfMeasure: VexFlowMeasure = topMeasure as VexFlowMeasure;
-        vfMeasure.addMeasureLine(lineType, linePosition);
+        const vfTopMeasure: VexFlowMeasure = topMeasure as VexFlowMeasure;
+        let renderInitialLine: boolean = false;
+
         if (bottomMeasure) {
-          // ToDo: feature/Repetitions
-          // create here the correct lines according to the given lineType.
-          (bottomMeasure as VexFlowMeasure).lineTo(topMeasure as VexFlowMeasure, VexFlowConverter.line(lineType, linePosition));
-          (bottomMeasure as VexFlowMeasure).addMeasureLine(lineType, linePosition);
+            renderInitialLine = true;
+            // ToDo: feature/Repetitions
+            // create here the correct lines according to the given lineType.
+            (bottomMeasure as VexFlowMeasure).lineTo(topMeasure as VexFlowMeasure, VexFlowConverter.line(lineType, linePosition));
+            (bottomMeasure as VexFlowMeasure).addMeasureLine(lineType, linePosition);
         }
+        if (vfTopMeasure) {
+            vfTopMeasure.addMeasureLine(lineType, linePosition, renderInitialLine);
+        }
+
         return new SystemLine(lineType, linePosition, this, topMeasure, bottomMeasure);
     }
 

+ 31 - 16
src/MusicalScore/Graphical/VexFlow/VexFlowOctaveShift.ts

@@ -1,10 +1,10 @@
-import Vex = require("vexflow");
+import Vex from "vexflow";
 import { GraphicalOctaveShift } from "../GraphicalOctaveShift";
 import { OctaveShift, OctaveEnum } from "../../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
 import { BoundingBox } from "../BoundingBox";
 import { GraphicalStaffEntry } from "../GraphicalStaffEntry";
 import { VexFlowVoiceEntry } from "./VexFlowVoiceEntry";
-import * as log from "loglevel";
+import log from "loglevel";
 
 /**
  * The vexflow adaptation of a graphical shift.
@@ -12,11 +12,11 @@ import * as log from "loglevel";
 export class VexFlowOctaveShift extends GraphicalOctaveShift {
 
     /** Defines the note where the octave shift starts */
-    private startNote: Vex.Flow.StemmableNote;
+    public startNote: Vex.Flow.StemmableNote;
     /** Defines the note where the octave shift ends */
-    private endNote: Vex.Flow.StemmableNote;
+    public endNote: Vex.Flow.StemmableNote;
     /** Top or bottom of the staffline */
-    private position: string;
+    private position: Vex.Flow.TextBracket.Positions;
     /** Supscript is a smaller text after the regular text (e.g. va after 8) */
     private supscript: string;
     /** Main text element */
@@ -31,22 +31,22 @@ export class VexFlowOctaveShift extends GraphicalOctaveShift {
         super(octaveShift, parent);
         switch (octaveShift.Type) {
             case OctaveEnum.VA8:
-                this.position = "top";
+                this.position = Vex.Flow.TextBracket.Positions.TOP;
                 this.supscript = "va";
                 this.text = "8";
                 break;
-                case OctaveEnum.MA15:
-                this.position = "top";
+            case OctaveEnum.MA15:
+                this.position = Vex.Flow.TextBracket.Positions.TOP;
                 this.supscript = "ma";
                 this.text = "15";
                 break;
-                case OctaveEnum.VB8:
-                this.position = "bottom";
+            case OctaveEnum.VB8:
+                this.position = Vex.Flow.TextBracket.Positions.BOTTOM;
                 this.supscript = "vb";
                 this.text = "8";
                 break;
-                case OctaveEnum.MB15:
-                this.position = "bottom";
+            case OctaveEnum.MB15:
+                this.position = Vex.Flow.TextBracket.Positions.BOTTOM;
                 this.supscript = "mb";
                 this.text = "15";
                 break;
@@ -60,16 +60,31 @@ export class VexFlowOctaveShift extends GraphicalOctaveShift {
      * Set a start note using a staff entry
      * @param graphicalStaffEntry the staff entry that holds the start note
      */
-    public setStartNote(graphicalStaffEntry: GraphicalStaffEntry): void {
-        this.startNote = (graphicalStaffEntry.graphicalVoiceEntries[0] as VexFlowVoiceEntry).vfStaveNote;
+    public setStartNote(graphicalStaffEntry: GraphicalStaffEntry): boolean {
+        for (const gve of graphicalStaffEntry.graphicalVoiceEntries) {
+            const vve: VexFlowVoiceEntry = (gve as VexFlowVoiceEntry);
+            if (vve?.vfStaveNote) {
+                this.startNote = vve.vfStaveNote;
+                return true;
+            }
+        }
+        return false; // couldn't find a startNote
     }
 
     /**
      * Set an end note using a staff entry
      * @param graphicalStaffEntry the staff entry that holds the end note
      */
-    public setEndNote(graphicalStaffEntry: GraphicalStaffEntry): void {
-        this.endNote = (graphicalStaffEntry.graphicalVoiceEntries[0] as VexFlowVoiceEntry).vfStaveNote;
+    public setEndNote(graphicalStaffEntry: GraphicalStaffEntry): boolean {
+        // this is duplicate code from setStartNote, but if we make one general method, we add a lot of branching.
+        for (const gve of graphicalStaffEntry.graphicalVoiceEntries) {
+            const vve: VexFlowVoiceEntry = (gve as VexFlowVoiceEntry);
+            if (vve?.vfStaveNote) {
+                this.endNote = vve.vfStaveNote;
+                return true;
+            }
+        }
+        return false; // couldn't find an endNote
     }
 
     /**

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

@@ -1,4 +1,4 @@
-import Vex = require("vexflow");
+import Vex from "vexflow";
 import { Slur } from "../../VoiceData/Expressions/ContinuousExpressions/Slur";
 
 export interface ICurveOptions {
@@ -55,7 +55,7 @@ export class VexFlowSlur {
     }
 
     // public createVexFlowCurve(): void {
-    //     if (this.voiceentrySlurStart !== undefined || this.voiceentrySlurEnd !== undefined) {
+    //     if (this.voiceentrySlurStart || this.voiceentrySlurEnd) {
     //         this.vfCurve = new Vex.Flow.Curve( (this.voiceentrySlurStart as VexFlowVoiceEntry).vfStaveNote,
     //                                            (this.voiceentrySlurEnd as VexFlowVoiceEntry).vfStaveNote,
     //                                            this.curve_Options()

+ 13 - 6
src/MusicalScore/Graphical/VexFlow/VexFlowStaffEntry.ts

@@ -1,11 +1,10 @@
-import Vex = require("vexflow");
+import Vex from "vexflow";
 import { GraphicalStaffEntry } from "../GraphicalStaffEntry";
 import { VexFlowMeasure } from "./VexFlowMeasure";
 import { SourceStaffEntry } from "../../VoiceData/SourceStaffEntry";
 import { unitInPixels } from "./VexFlowMusicSheetDrawer";
 import { VexFlowVoiceEntry } from "./VexFlowVoiceEntry";
 import { Note } from "../../VoiceData/Note";
-import { EngravingRules } from "../EngravingRules";
 
 export class VexFlowStaffEntry extends GraphicalStaffEntry {
     constructor(measure: VexFlowMeasure, sourceStaffEntry: SourceStaffEntry, staffEntryParent: VexFlowStaffEntry) {
@@ -30,15 +29,23 @@ export class VexFlowStaffEntry extends GraphicalStaffEntry {
         for (const gve of this.graphicalVoiceEntries as VexFlowVoiceEntry[]) {
             if (gve.vfStaveNote) {
                 gve.vfStaveNote.setStave(stave);
-                if (!gve.vfStaveNote.preFormatted || gve.vfStaveNote.getBoundingBox() === null) {
+                if (!gve.vfStaveNote.preFormatted) {
                     continue;
                 }
                 gve.applyBordersFromVexflow();
-                this.PositionAndShape.RelativePosition.x = gve.vfStaveNote.getBoundingBox().x / unitInPixels;
+                if (this.parentMeasure.ParentStaff.isTab) {
+                    // the x-position could be finetuned for the cursor.
+                    // somehow, gve.vfStaveNote.getBoundingBox() is null for a TabNote (which is a StemmableNote).
+                    this.PositionAndShape.RelativePosition.x = (gve.vfStaveNote.getAbsoluteX() + (<any>gve.vfStaveNote).glyph.getWidth()) / unitInPixels;
+                } else {
+                    this.PositionAndShape.RelativePosition.x = gve.vfStaveNote.getBoundingBox().getX() / unitInPixels;
+                }
                 const sourceNote: Note = gve.notes[0].sourceNote;
-                if (sourceNote.isRest() && sourceNote.Length.WholeValue === 1) { // whole rest
+                if (sourceNote.isRest() && sourceNote.Length.RealValue === this.parentMeasure.parentSourceMeasure.ActiveTimeSignature.RealValue) {
+                    // whole rest: length = measure length. (4/4 in a 4/4 time signature, 3/4 in a 3/4 time signature, 1/4 in a 1/4 time signature, etc.)
+                    // see Note.isWholeRest(), which is currently not safe
                     this.PositionAndShape.RelativePosition.x +=
-                        EngravingRules.Rules.WholeRestXShiftVexflow - 0.1; // xShift from VexFlowConverter
+                        this.parentMeasure.parentSourceMeasure.Rules.WholeRestXShiftVexflow - 0.1; // xShift from VexFlowConverter
                     gve.PositionAndShape.BorderLeft = -0.7;
                     gve.PositionAndShape.BorderRight = 0.7;
                 }

+ 7 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowStaffLine.ts

@@ -2,13 +2,16 @@ import {StaffLine} from "../StaffLine";
 import {MusicSystem} from "../MusicSystem";
 import {Staff} from "../../VoiceData/Staff";
 import { VexFlowSlur } from "./VexFlowSlur";
+import { AlignmentManager } from "./AlignmentManager";
 
 export class VexFlowStaffLine extends StaffLine {
     constructor(parentSystem: MusicSystem, parentStaff: Staff) {
         super(parentSystem, parentStaff);
+        this.alignmentManager = new AlignmentManager(this);
     }
 
     protected slursInVFStaffLine: VexFlowSlur[] = [];
+    protected alignmentManager: AlignmentManager;
 
     public get SlursInVFStaffLine(): VexFlowSlur[] {
         return this.slursInVFStaffLine;
@@ -16,4 +19,8 @@ export class VexFlowStaffLine extends StaffLine {
     public addVFSlurToVFStaffline(vfSlur: VexFlowSlur): void {
         this.slursInVFStaffLine.push(vfSlur);
     }
+
+    public get AlignmentManager(): AlignmentManager {
+        return this.alignmentManager;
+    }
 }

+ 119 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowTabMeasure.ts

@@ -0,0 +1,119 @@
+import Vex from "vexflow";
+import { Staff } from "../../VoiceData/Staff";
+import { SourceMeasure } from "../../VoiceData/SourceMeasure";
+import { VexFlowMeasure } from "./VexFlowMeasure";
+import { VexFlowStaffEntry } from "./VexFlowStaffEntry";
+import { VexFlowConverter } from "./VexFlowConverter";
+import { StaffLine } from "../StaffLine";
+import { GraphicalVoiceEntry } from "../GraphicalVoiceEntry";
+import { VexFlowVoiceEntry } from "./VexFlowVoiceEntry";
+import { Arpeggio } from "../../VoiceData/Arpeggio";
+import { Voice } from "../../VoiceData/Voice";
+import log from "loglevel";
+
+export class VexFlowTabMeasure extends VexFlowMeasure {
+    constructor(staff: Staff, sourceMeasure: SourceMeasure = undefined, staffLine: StaffLine = undefined) {
+        super(staff, sourceMeasure, staffLine);
+        this.isTabMeasure = true;
+    }
+
+    /**
+     * Reset all the geometric values and parameters of this measure and put it in an initialized state.
+     * This is needed to evaluate a measure a second time by system builder.
+     */
+    public resetLayout(): void {
+        // Take into account some space for the begin and end lines of the stave
+        // Will be changed when repetitions will be implemented
+        //this.beginInstructionsWidth = 20 / UnitInPixels;
+        //this.endInstructionsWidth = 20 / UnitInPixels;
+        this.stave = new Vex.Flow.TabStave(0, 0, 0, {
+            space_above_staff_ln: 0,
+            space_below_staff_ln: 0,
+        });
+        // also see VexFlowMusicSheetDrawer.drawSheet() for some other vexflow default value settings (like default font scale)
+        this.updateInstructionWidth();
+    }
+
+    public graphicalMeasureCreatedCalculations(): void {
+        for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
+            const graphicalStaffEntry: VexFlowStaffEntry = (this.staffEntries[idx] as VexFlowStaffEntry);
+
+            // create vex flow Notes:
+            for (const gve of graphicalStaffEntry.graphicalVoiceEntries) {
+                if (gve.notes[0].sourceNote.isRest()) {
+                    (gve as VexFlowVoiceEntry).vfStaveNote = VexFlowConverter.GhostNote(gve.notes[0].sourceNote.Length);
+                } else {
+                    (gve as VexFlowVoiceEntry).vfStaveNote = VexFlowConverter.CreateTabNote(gve);
+                }
+            }
+        }
+
+        if (this.rules.TupletNumbersInTabs) { // default false, don't show tuplets in tab measures
+            this.finalizeTuplets();
+        }
+
+        const voices: Voice[] = this.getVoicesWithinMeasure();
+
+        for (const voice of voices) {
+            if (!voice) {
+                continue;
+            }
+
+            // add a vexFlow voice for this voice:
+            this.vfVoices[voice.VoiceId] = new Vex.Flow.Voice({
+                        beat_value: this.parentSourceMeasure.Duration.Denominator,
+                        num_beats: this.parentSourceMeasure.Duration.Numerator,
+                        resolution: Vex.Flow.RESOLUTION,
+                    }).setMode(Vex.Flow.Voice.Mode.SOFT);
+
+            const restFilledEntries: GraphicalVoiceEntry[] =  this.getRestFilledVexFlowStaveNotesPerVoice(voice);
+            // create vex flow voices and add tickables to it:
+            for (const voiceEntry of restFilledEntries) {
+                if (voiceEntry.parentVoiceEntry) {
+                    if (voiceEntry.parentVoiceEntry.IsGrace && !voiceEntry.parentVoiceEntry.GraceAfterMainNote) {
+                        continue;
+                    }
+                }
+
+                const vexFlowVoiceEntry: VexFlowVoiceEntry = voiceEntry as VexFlowVoiceEntry;
+                if (voiceEntry.notes.length === 0 || !voiceEntry.notes[0] || !voiceEntry.notes[0].sourceNote.PrintObject) {
+                    // GhostNote, don't add modifiers like in-measure clefs
+                    this.vfVoices[voice.VoiceId].addTickable(vexFlowVoiceEntry.vfStaveNote);
+                    continue;
+                }
+
+                // don't add non-tab fingerings for tab measures (doesn't work yet for tabnotes in vexflow, see VexFlowMeasure.createFingerings())
+                // if (voiceEntry.parentVoiceEntry && this.rules.RenderFingerings) {
+                //     this.createFingerings(voiceEntry);
+                // }
+
+                // add Arpeggio
+                if (voiceEntry.parentVoiceEntry && voiceEntry.parentVoiceEntry.Arpeggio) {
+                    const arpeggio: Arpeggio = voiceEntry.parentVoiceEntry.Arpeggio;
+                    // TODO right now our arpeggio object has all arpeggio notes from arpeggios across all voices.
+                    // see VoiceGenerator. Doesn't matter for Vexflow for now though
+                    if (voiceEntry.notes && voiceEntry.notes.length > 1) {
+                        const type: Vex.Flow.Stroke.Type = VexFlowConverter.StrokeTypeFromArpeggioType(arpeggio.type);
+                        const stroke: Vex.Flow.Stroke = new Vex.Flow.Stroke(type, {
+                            all_voices: this.rules.ArpeggiosGoAcrossVoices
+                            // default: false. This causes arpeggios to always go across all voices, which is often unwanted.
+                            // also, this can cause infinite height of stroke, see #546
+                        });
+                        //if (arpeggio.notes.length === vexFlowVoiceEntry.notes.length) { // different workaround for endless y bug
+                        if (this.rules.RenderArpeggios) {
+                            vexFlowVoiceEntry.vfStaveNote.addStroke(0, stroke);
+                        }
+                    } else {
+                        log.debug(`[OSMD] arpeggio in measure ${this.MeasureNumber} could not be drawn.
+                        voice entry had less than two notes, arpeggio is likely between voice entries, not currently supported in Vexflow.`);
+                        // TODO: create new arpeggio with all the arpeggio's notes (arpeggio.notes), perhaps with GhostNotes in a new vfStaveNote. not easy.
+                    }
+                }
+
+                this.vfVoices[voice.VoiceId].addTickable(vexFlowVoiceEntry.vfStaveNote);
+            }
+        }
+        //this.createArticulations();
+        //this.createOrnaments();
+    }
+}

+ 8 - 3
src/MusicalScore/Graphical/VexFlow/VexFlowTextMeasurer.ts

@@ -2,19 +2,22 @@ import {ITextMeasurer} from "../../Interfaces/ITextMeasurer";
 import {Fonts} from "../../../Common/Enums/Fonts";
 import {FontStyles} from "../../../Common/Enums/FontStyles";
 import {VexFlowConverter} from "./VexFlowConverter";
+import { EngravingRules } from "../EngravingRules";
 /**
  * Created by Matthias on 21.06.2016.
  */
 
 export class VexFlowTextMeasurer implements ITextMeasurer {
-    constructor() {
+    constructor(rules: EngravingRules) {
         const canvas: HTMLCanvasElement = document.createElement("canvas");
         this.context = canvas.getContext("2d");
+        this.rules = rules;
     }
     // The context of a canvas used internally to compute font sizes
     private context: CanvasRenderingContext2D;
     public fontSize: number = 20;
     public fontSizeStandard: number = this.fontSize;
+    private rules: EngravingRules;
 
     /**
      *
@@ -23,8 +26,10 @@ export class VexFlowTextMeasurer implements ITextMeasurer {
      * @param style
      * @returns {number}
      */
-    public computeTextWidthToHeightRatio(text: string, font: Fonts, style: FontStyles, fontSize: number = this.fontSize): number {
-        this.context.font = VexFlowConverter.font(fontSize, style, font);
+    public computeTextWidthToHeightRatio(text: string, font: Fonts, style: FontStyles,
+                                         fontFamily: string = undefined,
+                                         fontSize: number = this.fontSize): number {
+        this.context.font = VexFlowConverter.font(fontSize, style, font, this.rules, fontFamily);
         return this.context.measureText(text).width / fontSize;
     }
 

+ 141 - 1
src/MusicalScore/Graphical/VexFlow/VexFlowVoiceEntry.ts

@@ -1,8 +1,12 @@
-import Vex = require("vexflow");
+import Vex from "vexflow";
 import { VoiceEntry } from "../../VoiceData/VoiceEntry";
 import { GraphicalVoiceEntry } from "../GraphicalVoiceEntry";
 import { GraphicalStaffEntry } from "../GraphicalStaffEntry";
 import { unitInPixels } from "./VexFlowMusicSheetDrawer";
+import { NoteEnum } from "../../../Common/DataObjects/Pitch";
+import { Note } from "../../VoiceData/Note";
+import { ColoringModes } from "./../DrawingParameters";
+import { GraphicalNote } from "../GraphicalNote";
 
 export class VexFlowVoiceEntry extends GraphicalVoiceEntry {
     private mVexFlowStaveNote: Vex.Flow.StemmableNote;
@@ -33,4 +37,140 @@ export class VexFlowVoiceEntry extends GraphicalVoiceEntry {
     public get vfStaveNote(): Vex.Flow.StemmableNote {
         return this.mVexFlowStaveNote;
     }
+
+    /** (Re-)color notes and stems by setting their Vexflow styles.
+     * Could be made redundant by a Vexflow PR, but Vexflow needs more solid and permanent color methods/variables for that
+     * See VexFlowConverter.StaveNote()
+     */
+    public color(): void {
+        const defaultColorNotehead: string = this.rules.DefaultColorNotehead;
+        const defaultColorRest: string = this.rules.DefaultColorRest;
+        const defaultColorStem: string = this.rules.DefaultColorStem;
+        const transparentColor: string = "#00000000"; // transparent color in vexflow
+        let noteheadColor: string; // if null: no noteheadcolor to set (stays black)
+        let sourceNoteNoteheadColor: string;
+
+        const vfStaveNote: any = (<VexFlowVoiceEntry>(this as any)).vfStaveNote;
+        for (let i: number = 0; i < this.notes.length; i++) {
+            const note: GraphicalNote = this.notes[i];
+
+            sourceNoteNoteheadColor = note.sourceNote.NoteheadColor;
+            noteheadColor = sourceNoteNoteheadColor;
+            // Switch between XML colors and automatic coloring
+            if (this.rules.ColoringMode === ColoringModes.AutoColoring ||
+                this.rules.ColoringMode === ColoringModes.CustomColorSet) {
+                if (note.sourceNote.isRest()) {
+                    noteheadColor = this.rules.ColoringSetCurrent.getValue(-1);
+                } else {
+                    const fundamentalNote: NoteEnum = note.sourceNote.Pitch.FundamentalNote;
+                    noteheadColor = this.rules.ColoringSetCurrent.getValue(fundamentalNote);
+                }
+            }
+            if (!note.sourceNote.PrintObject) {
+                noteheadColor = transparentColor; // transparent
+            } else if (!noteheadColor // revert transparency after PrintObject was set to false, then true again
+                || noteheadColor === "#000000" // questionable, because you might want to set specific notes to black,
+                                               // but unfortunately some programs export everything explicitly as black
+                ) {
+                noteheadColor = this.rules.DefaultColorNotehead;
+            }
+
+            // DEBUG runtime coloring test
+            /*const testColor: string = "#FF0000";
+            if (i === 2 && Math.random() < 0.1 && note.sourceNote.NoteheadColor !== testColor) {
+                const measureNumber: number = note.parentVoiceEntry.parentStaffEntry.parentMeasure.MeasureNumber;
+                noteheadColor = testColor;
+                console.log("color changed to " + noteheadColor + " of this note:\n" + note.sourceNote.Pitch.ToString() +
+                    ", in measure #" + measureNumber);
+            }*/
+
+            if (!sourceNoteNoteheadColor && this.rules.ColoringMode === ColoringModes.XML && note.sourceNote.PrintObject) {
+                if (!note.sourceNote.isRest() && defaultColorNotehead) {
+                    noteheadColor = defaultColorNotehead;
+                } else if (note.sourceNote.isRest() && defaultColorRest) {
+                    noteheadColor = defaultColorRest;
+                }
+            }
+            if (noteheadColor && note.sourceNote.PrintObject) {
+                note.sourceNote.NoteheadColorCurrentlyRendered = noteheadColor;
+            } else if (!noteheadColor) {
+                continue;
+            }
+
+            // color notebeam if all noteheads have same color and stem coloring enabled
+            if (this.rules.ColoringEnabled && note.sourceNote.NoteBeam && this.rules.ColorBeams) {
+                const beamNotes: Note[] = note.sourceNote.NoteBeam.Notes;
+                let colorBeam: boolean = true;
+                for (let j: number = 0; j < beamNotes.length; j++) {
+                    if (beamNotes[j].NoteheadColorCurrentlyRendered !== noteheadColor) {
+                        colorBeam = false;
+                    }
+                }
+                if (colorBeam) {
+                    if (vfStaveNote.beam !== null && vfStaveNote.beam.setStyle) {
+                        vfStaveNote.beam.setStyle({ fillStyle: noteheadColor, strokeStyle: noteheadColor});
+                    }
+                }
+            }
+
+            if (vfStaveNote) {
+                if (vfStaveNote.note_heads) { // see VexFlowConverter, needs Vexflow PR
+                    const notehead: any = vfStaveNote.note_heads[i];
+                    if (notehead) {
+                        notehead.setStyle({ fillStyle: noteheadColor, strokeStyle: noteheadColor });
+                    }
+                }
+                // set ledger line color. TODO coordinate this with VexFlowConverter.StaveNote(), where there's also still code for this, maybe unnecessarily.
+                if ((vfStaveNote as any).setLedgerLineStyle) { // setLedgerLineStyle doesn't exist on TabNote or rest, would throw error.
+                    if (noteheadColor === transparentColor) {
+                        (vfStaveNote as any).setLedgerLineStyle(
+                            { fillStyle: noteheadColor, strokeStyle: noteheadColor, lineWidth: this.rules.LedgerLineWidth });
+                    } else {
+                        (vfStaveNote as any).setLedgerLineStyle({
+                            fillStyle: this.rules.LedgerLineColorDefault,
+                            lineWidth: this.rules.LedgerLineWidth,
+                            strokeStyle: this.rules.LedgerLineColorDefault
+                        });
+                        // we could give the color (style) as noteheadColor, but then we need to figure out which note has the ledger line.
+                        // otherwise ledger lines get the color of the top note, see Function Test Color.
+                    }
+                }
+            }
+        }
+
+        // color stems
+        let stemColor: string = defaultColorStem; // reset to black/default when coloring was disabled. maybe needed elsewhere too
+        if (this.rules.ColoringEnabled) {
+            stemColor = this.parentVoiceEntry.StemColor; // TODO: once coloringSetCustom gets stem color, respect it
+            if (!stemColor
+                || stemColor === "#000000") { // see above, noteheadColor === "#000000"
+                stemColor = defaultColorStem;
+            }
+            if (this.rules.ColorStemsLikeNoteheads && noteheadColor) {
+                // condition could be even more fine-grained by only recoloring if there was no custom StemColor set. will be more complex though
+                stemColor = noteheadColor;
+            }
+        }
+        let stemTransparent: boolean = true;
+        for (const note of this.parentVoiceEntry.Notes) {
+            if (note.PrintObject) {
+                stemTransparent = false;
+                break;
+            }
+        }
+        if (stemTransparent) {
+            stemColor = transparentColor;
+        }
+        const stemStyle: Object = { fillStyle: stemColor, strokeStyle: stemColor };
+
+        if (vfStaveNote && vfStaveNote.setStemStyle) {
+            if (!stemTransparent) {
+                this.parentVoiceEntry.StemColor = stemColor;
+            }
+            vfStaveNote.setStemStyle(stemStyle);
+            if (vfStaveNote.flag && vfStaveNote.setFlagStyle && this.rules.ColorFlags) {
+                vfStaveNote.setFlagStyle(stemStyle);
+            }
+        }
+    }
 }

+ 176 - 0
src/MusicalScore/Graphical/VexFlow/VexflowStafflineNoteCalculator.ts

@@ -0,0 +1,176 @@
+import { IStafflineNoteCalculator } from "../../Interfaces/IStafflineNoteCalculator";
+import { GraphicalNote } from "../GraphicalNote";
+import { Pitch, NoteEnum } from "../../../Common/DataObjects/Pitch";
+import { VexFlowGraphicalNote } from "./VexFlowGraphicalNote";
+import { Dictionary } from "typescript-collections";
+import { EngravingRules } from "../EngravingRules";
+import { ClefEnum } from "../../VoiceData/Instructions/ClefInstruction";
+import { StemDirectionType, VoiceEntry } from "../../VoiceData/VoiceEntry";
+
+export class VexflowStafflineNoteCalculator implements IStafflineNoteCalculator {
+    private rules: EngravingRules;
+    private staffPitchListMapping: Dictionary<number, Array<Pitch>> = new Dictionary<number, Array<Pitch>>();
+    //These render on the single line by default
+    private baseLineNote: NoteEnum = NoteEnum.B;
+    private baseLineOctave: number = 1;
+
+    constructor(rules: EngravingRules) {
+        this.rules = rules;
+    }
+    /**
+     * This method is called for each note during the calc phase. We want to track all possible positions to make decisions
+     * during layout about where notes should be positioned.
+     * This directly notes that share a line to the same position, regardless of voice
+     * @param graphicalNote The note to be checked/positioned
+     * @param staffIndex The staffline the note is on
+     */
+    public trackNote(graphicalNote: GraphicalNote): void {
+        if (!(graphicalNote instanceof VexFlowGraphicalNote) || graphicalNote.Clef().ClefType !== ClefEnum.percussion ||
+        graphicalNote.sourceNote.isRest() || this.rules.PercussionOneLineCutoff === 0 ||
+        this.rules.PercussionForceVoicesOneLineCutoff === -1) {
+            return;
+        }
+        const staffIndex: number =
+                graphicalNote.parentVoiceEntry.parentStaffEntry.sourceStaffEntry.ParentStaff.idInMusicSheet;
+
+        let currentPitchList: Array<Pitch> = undefined;
+        if (!this.staffPitchListMapping.containsKey(staffIndex)) {
+            this.staffPitchListMapping.setValue(staffIndex, new Array<Pitch>());
+        }
+        currentPitchList = this.staffPitchListMapping.getValue(staffIndex);
+        const pitch: Pitch = graphicalNote.sourceNote.Pitch;
+        VexflowStafflineNoteCalculator.findOrInsert(currentPitchList, pitch);
+    }
+
+    private static PitchIndexOf(array: Array<Pitch>, pitch: Pitch, start: number = 0): number {
+        if (start > array.length - 1) {
+            return -1;
+        }
+
+        for (let i: number = start; i < array.length; i++) {
+            const p2: Pitch = array[i];
+            if (pitch.OperatorEquals(p2)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    private static findOrInsert(array: Array<Pitch>, pitch: Pitch): number {
+        for (let i: number = 0; i < array.length; i++) {
+            const p2: Pitch = array[i];
+            if (pitch.OperatorEquals(p2)) {
+                return i;
+            } else {
+                if (pitch.OperatorFundamentalLessThan(p2)) {
+                    array.splice(i, 0, pitch);
+                    return i;
+                }
+            }
+        }
+        //If we reach here, we've reached the end of the array.
+        //Means its the greatest pitch
+        array.push(pitch);
+        return array.length - 1;
+    }
+
+    /**
+     * This method is called for each note, and should make any necessary position changes based on the number of stafflines, clef, etc.
+     * @param graphicalNote The note to be checked/positioned
+     * @param staffIndex The staffline that this note exists on
+     * @returns the newly positioned note
+     */
+    public positionNote(graphicalNote: GraphicalNote): GraphicalNote {
+        const staffIndex: number =
+                graphicalNote.parentVoiceEntry.parentStaffEntry.sourceStaffEntry.ParentStaff.idInMusicSheet;
+
+        if (!(graphicalNote instanceof VexFlowGraphicalNote) || graphicalNote.sourceNote.isRest()
+            || !this.staffPitchListMapping.containsKey(staffIndex)) {
+            return graphicalNote;
+        }
+        const currentPitchList: Array<Pitch> = this.staffPitchListMapping.getValue(staffIndex);
+        //Don't need to position notes. We aren't under the cutoff
+        if (currentPitchList.length > this.rules.PercussionOneLineCutoff) {
+            return graphicalNote;
+        }
+        const vfGraphicalNote: VexFlowGraphicalNote = graphicalNote as VexFlowGraphicalNote;
+        const notePitch: Pitch = graphicalNote.sourceNote.Pitch;
+
+        //If we only need to render on one line
+        if (currentPitchList.length <= this.rules.PercussionForceVoicesOneLineCutoff) {
+            vfGraphicalNote.setAccidental(new Pitch(this.baseLineNote, this.baseLineOctave, notePitch.Accidental));
+        } else {
+            const pitchIndex: number = VexflowStafflineNoteCalculator.PitchIndexOf(currentPitchList, notePitch);
+            if (pitchIndex > -1) {
+                let fundamental: NoteEnum = this.baseLineNote;
+                let octave: number = this.baseLineOctave;
+                const half: number = Math.ceil(currentPitchList.length / 2);
+                //position above
+                if (pitchIndex >= half) {
+                    octave = 2;
+                    switch ((pitchIndex - half) % 5) {
+                        case 1:
+                            fundamental = NoteEnum.E;
+                            break;
+                        case 2:
+                            fundamental = NoteEnum.G;
+                            break;
+                        case 3:
+                            fundamental = NoteEnum.B;
+                            break;
+                        case 4:
+                            fundamental = NoteEnum.D;
+                            octave = 3;
+                            break;
+                        default:
+                            fundamental = NoteEnum.C;
+                            break;
+                    }
+                } else { //position below
+                    switch (pitchIndex % 5) {
+                        case 1:
+                            fundamental = NoteEnum.F;
+                            break;
+                        case 2:
+                            fundamental = NoteEnum.D;
+                            break;
+                        case 3:
+                            fundamental = NoteEnum.B;
+                            octave = 0;
+                            break;
+                        case 4:
+                            fundamental = NoteEnum.G;
+                            octave = 0;
+                            break;
+                        default:
+                            fundamental = NoteEnum.A;
+                            break;
+                    }
+                }
+                const mappedPitch: Pitch = new Pitch(fundamental, octave, notePitch.Accidental);
+                //Map the pitch, set stems properly
+                vfGraphicalNote.setAccidental(mappedPitch);
+                const parentVoiceEntry: VoiceEntry = vfGraphicalNote.parentVoiceEntry.parentVoiceEntry;
+                if (parentVoiceEntry.Notes.length < 2) { // Only switch stems if we aren't sharing stems with another note
+                    if (mappedPitch.Octave > this.baseLineOctave ||
+                        (mappedPitch.FundamentalNote === this.baseLineNote && mappedPitch.Octave === this.baseLineOctave)) {
+                        vfGraphicalNote.parentVoiceEntry.parentVoiceEntry.WantedStemDirection = StemDirectionType.Up;
+                    } else {
+                        vfGraphicalNote.parentVoiceEntry.parentVoiceEntry.WantedStemDirection = StemDirectionType.Down;
+                    }
+                }
+            }
+        }
+        return vfGraphicalNote;
+    }
+    /**
+     * Get the number of unique "voices" or note positions
+     * @param staffIndex The Staffline to get the count of
+     */
+    public getStafflineUniquePositionCount(staffIndex: number): number {
+        if (this.staffPitchListMapping.containsKey(staffIndex)) {
+            return this.staffPitchListMapping.getValue(staffIndex).length;
+        }
+        return 0;
+    }
+}

+ 2 - 1
src/MusicalScore/Graphical/index.ts

@@ -3,7 +3,7 @@
 export * from "./AbstractGraphicalExpression";
 export * from "./AbstractGraphicalInstruction";
 export * from "./AccidentalCalculator";
-export * from "./AlignmentManager";
+export * from "./VexFlow/AlignmentManager";
 export * from "./BoundingBox";
 export * from "./Clickable";
 export * from "./DrawingEnums";
@@ -49,3 +49,4 @@ export * from "./SystemLine";
 export * from "./SystemLinePosition";
 export * from "./SystemLinesEnum";
 export * from "./VerticalGraphicalStaffEntryContainer";
+export * from "./VexFlow";

+ 12 - 0
src/MusicalScore/Instrument.ts

@@ -29,6 +29,7 @@ export class Instrument extends InstrumentalGroup {
 
     private lyricVersesNumbers: number[] = [];
     private subInstruments: SubInstrument[] = [];
+    private partAbbreviation: string;
 
     public get Voices(): Voice[] {
         return this.voices;
@@ -103,6 +104,13 @@ export class Instrument extends InstrumentalGroup {
         }
         return undefined;
     }
+    public get PartAbbreviation(): string {
+        return this.partAbbreviation;
+    }
+    public set PartAbbreviation(value: string) {
+        this.partAbbreviation = value;
+    }
+
     public get Visible(): boolean {
         if (this.voices.length > 0) {
             return this.Voices[0].Visible;
@@ -242,4 +250,8 @@ export class Instrument extends InstrumentalGroup {
         }
     }
 
+    // necessary to be unique for MusicSystem.labels Dictionary
+    public toString(): string {
+        return `${this.Name} , id: ${this.id}, idstring: ${this.idString}`;
+    }
 }

+ 9 - 3
src/MusicalScore/Interfaces/IGraphicalSymbolFactory.ts

@@ -1,6 +1,5 @@
 import {ClefInstruction} from "../VoiceData/Instructions/ClefInstruction";
 import {Fraction} from "../../Common/DataObjects/Fraction";
-import {GraphicalMusicPage} from "../Graphical/GraphicalMusicPage";
 import {GraphicalNote} from "../Graphical/GraphicalNote";
 import {GraphicalStaffEntry} from "../Graphical/GraphicalStaffEntry";
 import {MusicSystem} from "../Graphical/MusicSystem";
@@ -15,15 +14,21 @@ import {GraphicalMeasure} from "../Graphical/GraphicalMeasure";
 import { TechnicalInstruction } from "../VoiceData/Instructions/TechnicalInstruction";
 import { GraphicalVoiceEntry } from "../Graphical/GraphicalVoiceEntry";
 import { VoiceEntry } from "../VoiceData/VoiceEntry";
+import { EngravingRules } from "../Graphical/EngravingRules";
+import { KeyInstruction } from "../VoiceData/Instructions/KeyInstruction";
 
 export interface IGraphicalSymbolFactory {
 
-    createMusicSystem(page: GraphicalMusicPage, systemIndex: number): MusicSystem;
+    createMusicSystem(systemIndex: number, rules: EngravingRules): MusicSystem;
 
     createStaffLine(parentSystem: MusicSystem, parentStaff: Staff): StaffLine;
 
     createGraphicalMeasure(sourceMeasure: SourceMeasure, staff: Staff): GraphicalMeasure;
 
+    createMultiRestMeasure(sourceMeasure: SourceMeasure, staff: Staff): GraphicalMeasure;
+
+    createTabStaffMeasure(sourceMeasure: SourceMeasure, staff: Staff): GraphicalMeasure;
+
     createExtraGraphicalMeasure(staffLine: StaffLine): GraphicalMeasure;
 
     createStaffEntry(sourceStaffEntry: SourceStaffEntry, measure: GraphicalMeasure): GraphicalStaffEntry;
@@ -54,8 +59,9 @@ export interface IGraphicalSymbolFactory {
 
     createInStaffClef(graphicalStaffEntry: GraphicalStaffEntry, clefInstruction: ClefInstruction): void;
 
-    createChordSymbol(
+    createChordSymbols(
         sourceStaffEntry: SourceStaffEntry,
         graphicalStaffEntry: GraphicalStaffEntry,
+        keyInstruction: KeyInstruction,
         transposeHalftones: number): void;
 }

+ 7 - 0
src/MusicalScore/Interfaces/IStafflineNoteCalculator.ts

@@ -0,0 +1,7 @@
+import { GraphicalNote } from "../Graphical/GraphicalNote";
+
+export interface IStafflineNoteCalculator {
+    trackNote(graphicalNote: GraphicalNote): void;
+    positionNote(graphicalNote: GraphicalNote): GraphicalNote;
+    getStafflineUniquePositionCount(staffIndex: number): number;
+}

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

@@ -4,6 +4,7 @@ import {FontStyles} from "../../Common/Enums/FontStyles";
 export interface ITextMeasurer {
     fontSize: number;
     fontSizeStandard: number;
-    computeTextWidthToHeightRatio(text: string, font: Fonts, style: FontStyles): number;
+    computeTextWidthToHeightRatio(text: string, font: Fonts, style: FontStyles,
+                                  fontFamily?: string, fontSize?: number): number;
     setFontSize(fontSize: number): number;
 }

+ 1 - 1
src/MusicalScore/Interfaces/ITextTranslation.ts

@@ -2,7 +2,7 @@ export class ITextTranslation {
     public static defaultTextTranslation: ITextTranslation;
 
     public static translateText(tag: string, text: string): string {
-        if (this.defaultTextTranslation === undefined) {
+        if (!this.defaultTextTranslation) {
             return text;
         }
 

+ 0 - 1
src/MusicalScore/Interfaces/ITransposeCalculator.ts

@@ -4,5 +4,4 @@ import {KeyInstruction} from "../VoiceData/Instructions/KeyInstruction";
 export interface ITransposeCalculator {
     transposePitch(pitch: Pitch, currentKeyInstruction: KeyInstruction, halftones: number): Pitch;
     transposeKey(keyInstruction: KeyInstruction, transpose: number): void;
-    getTransposedKeyString(keyInstruction: KeyInstruction, halftone: number): string;
 }

+ 7 - 2
src/MusicalScore/Label.ts

@@ -9,22 +9,27 @@ import {FontStyles} from "../Common/Enums/FontStyles";
  */
 export class Label {
 
-    constructor(text: string = "", alignment: TextAlignmentEnum = TextAlignmentEnum.CenterBottom, font: Fonts = Fonts.TimesNewRoman) {
+    constructor(text: string = "", alignment: TextAlignmentEnum = TextAlignmentEnum.CenterBottom,
+                font: Fonts = undefined, print: boolean = true) {
         this.text = text;
+        this.print = print;
         this.textAlignment = alignment;
         this.font = font;
+        this.fontFamily = undefined; // default value, will use EngravingRules.DefaultFontFamily at rendering
     }
 
     public text: string;
+    public print: boolean;
     public color: OSMDColor;
     public colorDefault: string; // TODO this is Vexflow format, convert to OSMDColor. for now convenient for default colors.
     public font: Fonts;
+    public fontFamily: string; // default undefined: will use EngravingRules.DefaultFontFamily at rendering
     public fontStyle: FontStyles;
     public fontHeight: number;
     public textAlignment: TextAlignmentEnum;
+    public IsCreditLabel: boolean = false;
 
     public ToString(): string {
         return this.text;
     }
-
 }

+ 8 - 8
src/MusicalScore/MusicParts/MusicPartManager.ts

@@ -25,7 +25,7 @@ export class MusicPartManager /*implements ISelectionListener*/ {
      * Main initialize method for MusicPartManager.
      */
     public init(): void {
-        this.parts = this.musicSheet.Repetitions.slice();
+        this.parts = this.musicSheet.Repetitions.slice(); // slice=arrayCopy
         this.sheetStart = this.musicSheet.SelectionStart = new Fraction(0, 1);
         this.sheetEnd = this.musicSheet.SelectionEnd = this.musicSheet.SheetEndTimestamp;
         this.calcMapping();
@@ -34,7 +34,7 @@ export class MusicPartManager /*implements ISelectionListener*/ {
         let curTransform: TimestampTransform = undefined;
         for (let i: number = this.timestamps.length - 1; i >= 0; i--) {
             curTransform = this.timestamps[i];
-            if (curEnrolledTimestamp >= curTransform.$from) {
+            if (curEnrolledTimestamp.gte(curTransform.$from)) {
                 return curTransform;
             }
         }
@@ -54,18 +54,18 @@ export class MusicPartManager /*implements ISelectionListener*/ {
         return this.musicSheet;
     }
     public getIterator(start?: Fraction): MusicPartManagerIterator {
-        if (start === undefined) {
-          return new MusicPartManagerIterator(this, this.musicSheet.SelectionStart, this.musicSheet.SelectionEnd);
+        if (!start) {
+            return new MusicPartManagerIterator(this.musicSheet, this.musicSheet.SelectionStart, this.musicSheet.SelectionEnd);
         }
-        return new MusicPartManagerIterator(this, start, undefined);
+        return new MusicPartManagerIterator(this.musicSheet, start, undefined);
     }
     public setSelectionStart(beginning: Fraction): void {
         this.musicSheet.SelectionStart = beginning;
         this.musicSheet.SelectionEnd = undefined;
     }
     public setSelectionRange(start: Fraction, end: Fraction): void {
-        this.musicSheet.SelectionStart = start === undefined ? this.sheetStart : start;
-        this.musicSheet.SelectionEnd = end === undefined ? this.sheetEnd : end;
+        this.musicSheet.SelectionStart = start ?? this.sheetStart;
+        this.musicSheet.SelectionEnd = end ?? this.sheetEnd;
     }
     private calcMapping(): void {
         const timestamps: TimestampTransform[] = [];
@@ -89,7 +89,7 @@ export class MusicPartManager /*implements ISelectionListener*/ {
                     curTimestampTransform.curRepetition = jumpRep;
                     curTimestampTransform.curRepetitionIteration = iterator.CurrentJumpResponsibleRepetitionIterationBeforeJump;
                     for (let i: number = this.timestamps.length - 2; i >= 0; i--) {
-                        if (timestamps[i].to.lt(jumpRep.AbsoluteTimestamp) || timestamps[i].curRepetition !== undefined) {
+                        if (timestamps[i].to.lt(jumpRep.AbsoluteTimestamp) || timestamps[i].curRepetition) {
                             break;
                         }
                         timestamps[i].nextBackJump = curTimestampTransform.nextBackJump;

+ 54 - 42
src/MusicalScore/MusicParts/MusicPartManagerIterator.ts

@@ -1,4 +1,3 @@
-import {MusicPartManager} from "./MusicPartManager";
 import {Fraction} from "../../Common/DataObjects/Fraction";
 import {Repetition} from "../MusicSource/Repetition";
 import {DynamicsContainer} from "../VoiceData/HelperObjects/DynamicsContainer";
@@ -14,26 +13,27 @@ import {ContinuousDynamicExpression} from "../VoiceData/Expressions/ContinuousEx
 import {InstantaneousDynamicExpression} from "../VoiceData/Expressions/InstantaneousDynamicExpression";
 import {MultiTempoExpression} from "../VoiceData/Expressions/MultiTempoExpression";
 import {AbstractExpression} from "../VoiceData/Expressions/AbstractExpression";
-import * as log from "loglevel";
+import log from "loglevel";
+import { MusicSheet } from "../MusicSheet";
 
 export class MusicPartManagerIterator {
-    constructor(manager: MusicPartManager, startTimestamp?: Fraction, endTimestamp?: Fraction) {
+    constructor(musicSheet: MusicSheet, startTimestamp?: Fraction, endTimestamp?: Fraction) {
         try {
             this.frontReached = true;
-            this.manager = manager;
+            this.musicSheet = musicSheet;
             this.currentVoiceEntries = undefined;
             this.frontReached = false;
-            for (const rep of manager.MusicSheet.Repetitions) {
+            for (const rep of this.musicSheet.Repetitions) {
                 this.setRepetitionIterationCount(rep, 1);
             }
-            this.activeDynamicExpressions = new Array(manager.MusicSheet.getCompleteNumberOfStaves());
-            this.currentMeasure = this.manager.MusicSheet.SourceMeasures[0];
-            if (startTimestamp === undefined) { return; }
+            this.activeDynamicExpressions = new Array(this.musicSheet.getCompleteNumberOfStaves());
+            this.currentMeasure = this.musicSheet.SourceMeasures[0];
+            if (!startTimestamp) { return; }
             do {
                 this.moveToNext();
-            } while ((this.currentVoiceEntries === undefined || this.currentTimeStamp.lt(startTimestamp)) && !this.endReached);
+            } while ((!this.currentVoiceEntries || this.currentTimeStamp.lt(startTimestamp)) && !this.endReached);
             for (let staffIndex: number = 0; staffIndex < this.activeDynamicExpressions.length; staffIndex++) {
-                if (this.activeDynamicExpressions[staffIndex] !== undefined) {
+                if (this.activeDynamicExpressions[staffIndex]) {
                     if (this.activeDynamicExpressions[staffIndex] instanceof ContinuousDynamicExpression) {
                         const continuousDynamic: ContinuousDynamicExpression =
                             <ContinuousDynamicExpression>this.activeDynamicExpressions[staffIndex];
@@ -53,7 +53,7 @@ export class MusicPartManagerIterator {
     }
     public backJumpOccurred: boolean;
     public forwardJumpOccurred: boolean;
-    private manager: MusicPartManager;
+    private musicSheet: MusicSheet;
     private currentMappingPart: MappingSourceMusicPart;
     private currentMeasure: SourceMeasure;
     private currentMeasureIndex: number = 0;
@@ -70,10 +70,12 @@ export class MusicPartManagerIterator {
     private currentRepetition: Repetition = undefined;
     private endReached: boolean = false;
     private frontReached: boolean = false;
-    private currentTimeStamp: Fraction = new Fraction(0, 1);
+    public currentTimeStamp: Fraction = new Fraction(0, 1);
     private currentEnrolledMeasureTimestamp: Fraction = new Fraction(0, 1);
+    private currentRelativeInMeasureTimestamp: Fraction = new Fraction(0, 1);
     private currentVerticalContainerInMeasureTimestamp: Fraction = new Fraction(0, 1);
     private jumpResponsibleRepetition: Repetition = undefined;
+    private currentBpm: number;
     private activeDynamicExpressions: AbstractExpression[] = [];
     private activeTempoExpression: MultiTempoExpression;
 
@@ -90,17 +92,20 @@ export class MusicPartManagerIterator {
         return this.currentRepetition;
     }
     public get CurrentRepetitionIteration(): number {
-        if (this.CurrentRepetition !== undefined) {
+        if (this.CurrentRepetition) {
             return this.getRepetitionIterationCount(this.CurrentRepetition);
         }
         return 0;
     }
     public get CurrentJumpResponsibleRepetitionIterationBeforeJump(): number {
-        if (this.jumpResponsibleRepetition !== undefined) {
+        if (this.jumpResponsibleRepetition) {
             return this.getRepetitionIterationCount(this.jumpResponsibleRepetition) - 1;
         }
         return 0;
     }
+    public get CurrentBpm(): number {
+        return this.currentBpm;
+    }
     public get CurrentVoiceEntries(): VoiceEntry[] {
         return this.currentVoiceEntries;
     }
@@ -113,6 +118,9 @@ export class MusicPartManagerIterator {
     public get CurrentSourceTimestamp(): Fraction {
         return this.currentTimeStamp;
     }
+    public get CurrentRelativeInMeasureTimestamp(): Fraction {
+        return this.currentRelativeInMeasureTimestamp;
+    }
     public get JumpOccurred(): boolean {
         return this.backJumpOccurred || this.forwardJumpOccurred;
     }
@@ -132,14 +140,16 @@ export class MusicPartManagerIterator {
     /**
      * Creates a clone of this iterator which has the same actual position.
      */
-    public clone(): MusicPartManagerIterator {
-        const ret: MusicPartManagerIterator = new MusicPartManagerIterator(this.manager);
+    public clone(startTimeStamp: Fraction = undefined, endTimeStamp: Fraction = undefined): MusicPartManagerIterator {
+        const ret: MusicPartManagerIterator = new MusicPartManagerIterator(this.musicSheet, startTimeStamp ?? this.currentTimeStamp, endTimeStamp);
         ret.currentVoiceEntryIndex = this.currentVoiceEntryIndex;
         ret.currentMappingPart = this.currentMappingPart;
         ret.currentPartIndex = this.currentPartIndex;
         ret.currentVoiceEntries = this.currentVoiceEntries;
         ret.endReached = this.endReached;
         ret.frontReached = this.frontReached;
+        // alternative method to set currentTimeStamp? may not fully affect current iterator position
+        // ret.currentTimeStamp = this.currentTimeStamp;
         return ret;
     }
 
@@ -150,10 +160,10 @@ export class MusicPartManagerIterator {
      */
     public CurrentVisibleVoiceEntries(instrument?: Instrument): VoiceEntry[] {
         const voiceEntries: VoiceEntry[] = [];
-        if (this.currentVoiceEntries === undefined) {
+        if (!this.currentVoiceEntries) {
             return voiceEntries;
         }
-        if (instrument !== undefined) {
+        if (instrument) {
             for (const entry of this.currentVoiceEntries) {
                 if (entry.ParentVoice.Parent.IdString === instrument.IdString) {
                     this.getVisibleEntries(entry, voiceEntries);
@@ -175,10 +185,10 @@ export class MusicPartManagerIterator {
      */
     public CurrentAudibleVoiceEntries(instrument?: Instrument): VoiceEntry[] {
         const voiceEntries: VoiceEntry[] = [];
-        if (this.currentVoiceEntries === undefined) {
+        if (!this.currentVoiceEntries) {
             return voiceEntries;
         }
-        if (instrument !== undefined) {
+        if (instrument) {
             for (const entry of this.currentVoiceEntries) {
                 if (entry.ParentVoice.Parent.IdString === instrument.IdString) {
                     this.getAudibleEntries(entry, voiceEntries);
@@ -209,10 +219,10 @@ export class MusicPartManagerIterator {
      */
     public CurrentScoreFollowingVoiceEntries(instrument?: Instrument): VoiceEntry[] {
         const voiceEntries: VoiceEntry[] = [];
-        if (this.currentVoiceEntries === undefined) {
+        if (!this.currentVoiceEntries) {
             return voiceEntries;
         }
-        if (instrument !== undefined) {
+        if (instrument) {
             for (const entry of this.currentVoiceEntries) {
                 if (entry.ParentVoice.Parent.IdString === instrument.IdString) {
                     this.getScoreFollowingEntries(entry, voiceEntries);
@@ -233,11 +243,11 @@ export class MusicPartManagerIterator {
     public moveToNext(): void {
         this.forwardJumpOccurred = this.backJumpOccurred = false;
         if (this.endReached) { return; }
-        if (this.currentVoiceEntries !== undefined) {
+        if (this.currentVoiceEntries) {
             this.currentVoiceEntries = [];
         }
         this.recursiveMove();
-        if (this.currentMeasure === undefined) {
+        if (!this.currentMeasure) {
             this.currentTimeStamp = new Fraction(99999, 1);
         }
     }
@@ -305,7 +315,7 @@ export class MusicPartManagerIterator {
     private moveDynamicIndexToTimestamp(absoluteTimestamp: Fraction): void {
         let dynamics: DynamicsContainer[] = this.manager.MusicSheet.TimestampSortedDynamicExpressionsList;
         for (let index: number = 0; index < dynamics.length; index++) {
-            if (dynamics[index].parMultiExpression().AbsoluteTimestamp >= absoluteTimestamp) {
+            if (dynamics[index].parMultiExpression().AbsoluteTimestamp.gte(absoluteTimestamp)) {
                 this.currentDynamicEntryIndex = Math.Max(0, index - 1);
                 return
             }
@@ -321,7 +331,7 @@ export class MusicPartManagerIterator {
     private handleRepetitionsAtMeasureBegin(): void {
         for (let idx: number = 0, len: number = this.currentMeasure.FirstRepetitionInstructions.length; idx < len; ++idx) {
             const repetitionInstruction: RepetitionInstruction = this.currentMeasure.FirstRepetitionInstructions[idx];
-            if (repetitionInstruction.parentRepetition === undefined) { continue; }
+            if (!repetitionInstruction.parentRepetition) { continue; }
             const currentRepetition: Repetition = repetitionInstruction.parentRepetition;
             this.currentRepetition = currentRepetition;
             if (currentRepetition.StartIndex === this.currentMeasureIndex) {
@@ -341,7 +351,7 @@ export class MusicPartManagerIterator {
         for (let idx: number = 0, len: number = this.currentMeasure.LastRepetitionInstructions.length; idx < len; ++idx) {
             const repetitionInstruction: RepetitionInstruction = this.currentMeasure.LastRepetitionInstructions[idx];
             const currentRepetition: Repetition = repetitionInstruction.parentRepetition;
-            if (currentRepetition === undefined) { continue; }
+            if (!currentRepetition) { continue; }
             if (currentRepetition.BackwardJumpInstructions.indexOf(repetitionInstruction) > -1) {
                 if (this.getRepetitionIterationCount(currentRepetition) < currentRepetition.UserNumberOfRepetitions) {
                     this.doBackJump(currentRepetition);
@@ -364,7 +374,7 @@ export class MusicPartManagerIterator {
                 );
                 if (forwardJumpTargetMeasureIndex >= 0) {
                     this.currentMeasureIndex = forwardJumpTargetMeasureIndex;
-                    this.currentMeasure = this.manager.MusicSheet.SourceMeasures[this.currentMeasureIndex];
+                    this.currentMeasure = this.musicSheet.SourceMeasures[this.currentMeasureIndex];
                     this.currentVoiceEntryIndex = -1;
                     this.jumpResponsibleRepetition = currentRepetition;
                     this.forwardJumpOccurred = true;
@@ -382,7 +392,7 @@ export class MusicPartManagerIterator {
     }
     private doBackJump(currentRepetition: Repetition): void {
         this.currentMeasureIndex = currentRepetition.getBackwardJumpTarget();
-        this.currentMeasure = this.manager.MusicSheet.SourceMeasures[this.currentMeasureIndex];
+        this.currentMeasure = this.musicSheet.SourceMeasures[this.currentMeasureIndex];
         this.currentVoiceEntryIndex = -1;
         this.incrementRepetitionIterationCount(currentRepetition);
         this.jumpResponsibleRepetition = currentRepetition;
@@ -397,13 +407,13 @@ export class MusicPartManagerIterator {
             for (let idx: number = 0, len: number = instructions.length; idx < len; ++idx) {
                 const abstractNotationInstruction: AbstractNotationInstruction = instructions[idx];
                 if (abstractNotationInstruction instanceof RhythmInstruction) {
-                    this.manager.MusicSheet.SheetPlaybackSetting.rhythm = (<RhythmInstruction>abstractNotationInstruction).Rhythm;
+                    this.musicSheet.SheetPlaybackSetting.rhythm = (<RhythmInstruction>abstractNotationInstruction).Rhythm;
                 }
             }
         }
     }
     private activateCurrentDynamicOrTempoInstructions(): void {
-        const timeSortedDynamics: DynamicsContainer[] = this.manager.MusicSheet.TimestampSortedDynamicExpressionsList;
+        const timeSortedDynamics: DynamicsContainer[] = this.musicSheet.TimestampSortedDynamicExpressionsList;
         while (
           this.currentDynamicEntryIndex > 0 && (
             this.currentDynamicEntryIndex >= timeSortedDynamics.length ||
@@ -425,9 +435,9 @@ export class MusicPartManagerIterator {
             const dynamicsContainer: DynamicsContainer = timeSortedDynamics[this.currentDynamicEntryIndex];
             const staffIndex: number = dynamicsContainer.staffNumber;
             if (this.CurrentSourceTimestamp.Equals(dynamicsContainer.parMultiExpression().AbsoluteTimestamp)) {
-                if (dynamicsContainer.continuousDynamicExpression !== undefined) {
+                if (dynamicsContainer.continuousDynamicExpression) {
                     this.activeDynamicExpressions[staffIndex] = dynamicsContainer.continuousDynamicExpression;
-                } else if (dynamicsContainer.instantaneousDynamicExpression !== undefined) {
+                } else if (dynamicsContainer.instantaneousDynamicExpression) {
                     this.activeDynamicExpressions[staffIndex] = dynamicsContainer.instantaneousDynamicExpression;
                 }
             }
@@ -435,7 +445,7 @@ export class MusicPartManagerIterator {
         }
         this.currentDynamicChangingExpressions = [];
         for (let staffIndex: number = 0; staffIndex < this.activeDynamicExpressions.length; staffIndex++) {
-            if (this.activeDynamicExpressions[staffIndex] !== undefined) {
+            if (this.activeDynamicExpressions[staffIndex]) {
                 let startTime: Fraction;
                 let endTime: Fraction;
                 if (this.activeDynamicExpressions[staffIndex] instanceof ContinuousDynamicExpression) {
@@ -453,7 +463,7 @@ export class MusicPartManagerIterator {
                 }
             }
         }
-        const timeSortedTempoExpressions: MultiTempoExpression[] = this.manager.MusicSheet.TimestampSortedTempoExpressionsList;
+        const timeSortedTempoExpressions: MultiTempoExpression[] = this.musicSheet.TimestampSortedTempoExpressionsList;
 
         while (this.currentTempoEntryIndex > 0 && (
           this.currentTempoEntryIndex >= timeSortedTempoExpressions.length
@@ -477,9 +487,9 @@ export class MusicPartManagerIterator {
             this.currentTempoEntryIndex++;
         }
         this.currentTempoChangingExpression = undefined;
-        if (this.activeTempoExpression !== undefined) {
+        if (this.activeTempoExpression) {
             let endTime: Fraction = this.activeTempoExpression.AbsoluteTimestamp;
-            if (this.activeTempoExpression.ContinuousTempo !== undefined) {
+            if (this.activeTempoExpression.ContinuousTempo) {
                 endTime = this.activeTempoExpression.ContinuousTempo.AbsoluteEndTimestamp;
             }
             if (   this.activeTempoExpression.AbsoluteTimestamp.lte(this.CurrentSourceTimestamp)
@@ -501,7 +511,9 @@ export class MusicPartManagerIterator {
             this.currentVoiceEntries = this.getVoiceEntries(currentContainer);
             this.currentVerticalContainerInMeasureTimestamp = currentContainer.Timestamp;
             this.currentTimeStamp = Fraction.plus(this.currentMeasure.AbsoluteTimestamp, this.currentVerticalContainerInMeasureTimestamp);
-            if (this.currentTimeStamp >= this.manager.MusicSheet.SelectionEnd) {
+            const selectionEnd: Fraction = this.musicSheet.SelectionEnd;
+            // TODO handle selectionEnd undefined, can happen in Beethoven Ferne Geliebte
+            if (selectionEnd && this.currentTimeStamp.gte(selectionEnd)) {
                 this.endReached = true;
             }
             this.activateCurrentDynamicOrTempoInstructions();
@@ -509,8 +521,8 @@ export class MusicPartManagerIterator {
         }
         this.currentEnrolledMeasureTimestamp.Add(this.currentMeasure.Duration);
         this.handleRepetitionsAtMeasureEnd();
-        if (this.currentMeasureIndex >= 0 && this.currentMeasureIndex < this.manager.MusicSheet.SourceMeasures.length) {
-            this.currentMeasure = this.manager.MusicSheet.SourceMeasures[this.currentMeasureIndex];
+        if (this.currentMeasureIndex >= 0 && this.currentMeasureIndex < this.musicSheet.SourceMeasures.length) {
+            this.currentMeasure = this.musicSheet.SourceMeasures[this.currentMeasureIndex];
             this.currentTimeStamp = Fraction.plus(this.currentMeasure.AbsoluteTimestamp, this.currentVerticalContainerInMeasureTimestamp);
             this.currentVoiceEntryIndex = -1;
             this.recursiveMove();
@@ -534,7 +546,7 @@ export class MusicPartManagerIterator {
             if (!notesOnly) { return true; }
             for (let idx: number = 0, len: number = tlist.length; idx < len; ++idx) {
                 const entry: VoiceEntry = tlist[idx];
-                if (entry.Notes[0].Pitch !== undefined) { return true; }
+                if (entry.Notes[0].Pitch) { return true; }
             }
         }
         return false;
@@ -557,7 +569,7 @@ export class MusicPartManagerIterator {
     private getVoiceEntries(container: VerticalSourceStaffEntryContainer): VoiceEntry[] {
         const entries: VoiceEntry[] = [];
         for (const sourceStaffEntry of container.StaffEntries) {
-            if (sourceStaffEntry === undefined) { continue; }
+            if (!sourceStaffEntry) { continue; }
             for (const voiceEntry of sourceStaffEntry.VoiceEntries) {
                 entries.push(voiceEntry);
             }

+ 77 - 46
src/MusicalScore/MusicSheet.ts

@@ -16,7 +16,7 @@ import {EngravingRules} from "./Graphical/EngravingRules";
 import {NoteState} from "./Graphical/DrawingEnums";
 import {Note} from "./VoiceData/Note";
 import {VoiceEntry} from "./VoiceData/VoiceEntry";
-import * as log from "loglevel";
+import log from "loglevel";
 
 // FIXME Andrea: Commented out some unnecessary/not-ported-yet code, have a look at (*)
 
@@ -32,7 +32,6 @@ export class PlaybackSettings {
  */
 export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet>*/ {
     constructor() {
-        this.rules = EngravingRules.Rules;
         this.playbackSettings = new PlaybackSettings();
         // FIXME?
         // initialize SheetPlaybackSetting with default values
@@ -41,21 +40,22 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
         this.pageWidth = 120;
         // create MusicPartManager
         this.MusicPartManager = new MusicPartManager(this);
+        this.hasBPMInfo = false;
     }
-    public static defaultTitle: string = "[kein Titel]";
+    public static defaultTitle: string = "[no title given]";
 
     public userStartTempoInBPM: number;
     public pageWidth: number;
-    public rules: EngravingRules;
 
-    private idString: string = "kjgdfuilhsdaöoihfsvjh";
+    private idString: string = "random idString, not initialized";
     private sourceMeasures: SourceMeasure[] = [];
     private repetitions: Repetition[] = [];
     private dynListStaves: DynamicsContainer[][] = [];
     private timestampSortedDynamicExpressionsList: DynamicsContainer[] = [];
     private timestampSortedTempoExpressionsList: MultiTempoExpression[] = [];
     private instrumentalGroups: InstrumentalGroup[] = [];
-    private instruments: Instrument[] = [];
+    /** The parts in the sheet, e.g. piano left hand, or piano right hand, or violin. */
+    private instruments: Instrument[] = []; // Should be renamed from instruments to parts and part, though this will be a big refactor
     private playbackSettings: PlaybackSettings;
     private path: string;
     private title: Label;
@@ -75,8 +75,10 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
     private hasBeenOpenedForTheFirstTime: boolean = false;
     private currentEnrolledPosition: Fraction = new Fraction(0, 1);
     // (*) private musicSheetParameterObject: MusicSheetParameterObject = undefined;
-    private engravingRules: EngravingRules;
+    private rules: EngravingRules;
     // (*) private musicSheetParameterChangedDelegate: MusicSheetParameterChangedDelegate;
+    /* Whether BPM info is present in the sheet. If it is set to false, each measure's BPM was set to a default of 120. */
+    private hasBPMInfo: boolean;
 
     /**
      * Get the global index within the music sheet for this staff.
@@ -110,13 +112,17 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
     public get InstrumentalGroups(): InstrumentalGroup[] {
         return this.instrumentalGroups;
     }
-    public get Instruments(): Instrument[] {
+    public get Parts(): Instrument[] { // Instrument should be renamed to Part
         return this.instruments;
     }
-     public get SheetPlaybackSetting(): PlaybackSettings {
+    public get Instruments(): Instrument[] { // deprecated
+        // this method name should remain for a while to maintain compatibility even when Instrument is renamed to Part
+        return this.instruments;
+    }
+    public get SheetPlaybackSetting(): PlaybackSettings {
         return this.playbackSettings;
     }
-     public set SheetPlaybackSetting(value: PlaybackSettings) {
+    public set SheetPlaybackSetting(value: PlaybackSettings) {
         this.playbackSettings = value;
     }
     public get DrawErroneousMeasures(): boolean {
@@ -152,33 +158,45 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
         return this.staves;
     }
     public get TitleString(): string {
-        if (this.title !== undefined) {
+        if (this.title) {
             return this.title.text;
         } else {
             return "";
         }
     }
+    public set TitleString(value: string) {
+        this.Title = new Label(value);
+    }
     public get SubtitleString(): string {
-        if (this.subtitle !== undefined) {
+        if (this.subtitle) {
             return this.subtitle.text;
         } else {
             return "";
         }
     }
+    public set SubtitleString(value: string) {
+        this.Subtitle = new Label(value);
+    }
     public get ComposerString(): string {
-        if (this.composer !== undefined) {
+        if (this.composer) {
             return this.composer.text;
         } else {
             return "";
         }
     }
+    public set ComposerString(value: string) {
+        this.Composer = new Label(value);
+    }
     public get LyricistString(): string {
-        if (this.lyricist !== undefined) {
+        if (this.lyricist) {
             return this.lyricist.text;
         } else {
             return "";
         }
     }
+    public set LyricistString(value: string) {
+        this.Lyricist = new Label(value);
+    }
     public get Title(): Label {
         return this.title;
     }
@@ -204,10 +222,14 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
         this.lyricist = value;
     }
     public get Rules(): EngravingRules {
-       return this.engravingRules;
+        if (!this.rules) {
+            log.debug("warning: sheet.Rules was undefined. Creating new EngravingRules.");
+            this.rules = new EngravingRules();
+        }
+        return this.rules;
     }
     public set Rules(value: EngravingRules) {
-       this.engravingRules = value;
+        this.rules = value;
     }
     public get SheetErrors(): MusicSheetErrors {
         return this.musicSheetErrors;
@@ -225,6 +247,15 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
     public set SelectionEnd(value: Fraction) {
         this.selectionEnd = value;
     }
+
+    public set HasBPMInfo(value: boolean) {
+        this.hasBPMInfo = value;
+    }
+
+    public get HasBPMInfo(): boolean {
+        return this.hasBPMInfo;
+    }
+
     // (*) public get MusicSheetParameterObject(): MusicSheetParameterObject {
     //    return this.musicSheetParameterObject;
     //}
@@ -319,17 +350,17 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
         return this.sourceMeasures[this.sourceMeasures.length - 1];
     }
     public resetAllNoteStates(): void {
-       const iterator: MusicPartManagerIterator = this.MusicPartManager.getIterator();
-       while (!iterator.EndReached && iterator.CurrentVoiceEntries !== undefined) {
-           for (let idx: number = 0, len: number = iterator.CurrentVoiceEntries.length; idx < len; ++idx) {
-               const voiceEntry: VoiceEntry = iterator.CurrentVoiceEntries[idx];
-               for (let idx2: number = 0, len2: number = voiceEntry.Notes.length; idx2 < len2; ++idx2) {
-                   const note: Note = voiceEntry.Notes[idx2];
-                   note.state = NoteState.Normal;
-               }
-           }
-           iterator.moveToNext();
-       }
+        const iterator: MusicPartManagerIterator = this.MusicPartManager.getIterator();
+        while (!iterator.EndReached && iterator.CurrentVoiceEntries) {
+            for (let idx: number = 0, len: number = iterator.CurrentVoiceEntries.length; idx < len; ++idx) {
+                const voiceEntry: VoiceEntry = iterator.CurrentVoiceEntries[idx];
+                for (let idx2: number = 0, len2: number = voiceEntry.Notes.length; idx2 < len2; ++idx2) {
+                    const note: Note = voiceEntry.Notes[idx2];
+                    note.state = NoteState.Normal;
+                }
+            }
+            iterator.moveToNext();
+        }
     }
     public getMusicSheetInstrumentIndex(instrument: Instrument): number {
         return this.Instruments.indexOf(instrument);
@@ -405,17 +436,17 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
     //    }
     //
     //}
-    //public GetExpressionsStartTempoInBPM(): number {
-    //    if (this.TimestampSortedTempoExpressionsList.length > 0) {
-    //        let me: MultiTempoExpression = this.TimestampSortedTempoExpressionsList[0];
-    //        if (me.InstantaneousTempo !== undefined) {
-    //            return me.InstantaneousTempo.TempoInBpm;
-    //        } else if (me.ContinuousTempo !== undefined) {
-    //            return me.ContinuousTempo.StartTempo;
-    //        }
-    //    }
-    //    return this.UserStartTempoInBPM;
-    //}
+    public getExpressionsStartTempoInBPM(): number {
+        if (this.TimestampSortedTempoExpressionsList.length > 0) {
+            const me: MultiTempoExpression = this.TimestampSortedTempoExpressionsList[0];
+            if (me.InstantaneousTempo) {
+                return me.InstantaneousTempo.TempoInBpm;
+            } else if (me.ContinuousTempo) {
+                return me.ContinuousTempo.StartTempo;
+            }
+        }
+        return this.userStartTempoInBPM;
+    }
     public get Errors(): { [n: number]: string[]; } {
         return this.musicSheetErrors.measureErrors;
     }
@@ -451,22 +482,22 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
     }
     // (*)
     //public SetMusicSheetParameter(parameter: MusicSheetParameters, value: Object): void {
-    //    if (this.PhonicScoreInterface !== undefined) {
+    //    if (this.PhonicScoreInterface) {
     //        this.PhonicScoreInterface.RequestMusicSheetParameter(parameter, value);
     //    } else {
     //        let oldValue: Object = 0;
-    //        if (parameter === undefined) { // FIXME MusicSheetParameters.MusicSheetTranspose) {
+    //        if (!parameter) { // FIXME MusicSheetParameters.MusicSheetTranspose) {
     //            oldValue = this.Transpose;
     //            this.Transpose = value;
     //        }
-    //        if (parameter === undefined) { // FIXME MusicSheetParameters.StartTempoInBPM) {
+    //        if (!parameter) { // FIXME MusicSheetParameters.StartTempoInBPM) {
     //            oldValue = this.UserStartTempoInBPM;
     //            this.UserStartTempoInBPM = value;
     //        }
-    //        if (parameter === undefined) { // FIXME MusicSheetParameters.HighlightErrors) {
+    //        if (!parameter) { // FIXME MusicSheetParameters.HighlightErrors) {
     //            oldValue = value;
     //        }
-    //        if (this.MusicSheetParameterChanged !== undefined) {
+    //        if (this.MusicSheetParameterChanged) {
     //            this.musicSheetParameterChangedDelegate(undefined, parameter, value, oldValue);
     //        }
     //    }
@@ -478,13 +509,13 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
     //    this.musicSheetParameterChangedDelegate = value;
     //}
     public get FullNameString(): string {
-       return this.ComposerString + " " + this.TitleString;
+        return this.ComposerString + " " + this.TitleString;
     }
     public get IdString(): string {
-       return this.idString;
+        return this.idString;
     }
     public set IdString(value: string) {
-       this.idString = value;
+        this.idString = value;
     }
     // (*)
     // public Dispose(): void {

+ 1 - 1
src/MusicalScore/MusicSource/MappingSourceMusicPart.ts

@@ -46,7 +46,7 @@ export class MappingSourceMusicPart /* implements IComparable, IComparable<Mappi
     }
     public CompareTo(comp: MappingSourceMusicPart): number {
         //let comp: MappingSourceMusicPart = <MappingSourceMusicPart>(obj, MappingSourceMusicPart);
-        if (comp !== undefined) {
+        if (comp) {
             return this.startTimestamp.CompareTo(comp.startTimestamp);
         } else { return 1; }
     }

+ 5 - 5
src/MusicalScore/MusicSource/Repetition.ts

@@ -4,7 +4,7 @@ import {Fraction} from "../../Common/DataObjects/Fraction";
 import {MusicSheet} from "../MusicSheet";
 import {RepetitionInstruction} from "../VoiceData/Instructions/RepetitionInstruction";
 import {PartListEntry} from "./PartListEntry";
-import * as log from "loglevel";
+import log from "loglevel";
 
 export class Repetition extends PartListEntry /*implements IRepetition*/ {
     constructor(musicSheet: MusicSheet, virtualOverallRepetition: boolean) {
@@ -65,7 +65,7 @@ export class Repetition extends PartListEntry /*implements IRepetition*/ {
     }
     public getForwardJumpTargetForIteration(iteration: number): number {
         const endingIndex: number = this.repetitonIterationOrder[iteration - 1];
-        if (this.endingIndexDict[endingIndex] !== undefined) {
+        if (this.endingIndexDict[endingIndex]) {
             return this.endingIndexDict[endingIndex].part.StartIndex;
         }
         return -1;
@@ -99,7 +99,7 @@ export class Repetition extends PartListEntry /*implements IRepetition*/ {
     //    }
     //}
     public setEndingEndIndex(endingNumber: number, endIndex: number): void {
-        if (this.endingIndexDict[endingNumber] !== undefined) {
+        if (this.endingIndexDict[endingNumber]) {
             this.endingIndexDict[endingNumber].part.setEndIndex(endIndex);
         }
     }
@@ -123,7 +123,7 @@ export class Repetition extends PartListEntry /*implements IRepetition*/ {
             return this.StartIndex;
         }
         let result: number = this.backwardJumpInstructions[this.backwardJumpInstructions.length - 1].measureIndex;
-        if (this.endingIndexDict[this.NumberOfEndings] !== undefined) {
+        if (this.endingIndexDict[this.NumberOfEndings]) {
             result = Math.max(this.endingIndexDict[this.NumberOfEndings].part.EndIndex, result);
         }
         return result;
@@ -136,7 +136,7 @@ export class Repetition extends PartListEntry /*implements IRepetition*/ {
             const sourceMeasure: SourceMeasure = this.musicSheet2.SourceMeasures[measureIndex];
             for (let i: number = 0; i < sourceMeasure.CompleteNumberOfStaves; i++) {
                 for (const sourceStaffEntry of sourceMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries) {
-                    if (sourceStaffEntry !== undefined) {
+                    if (sourceStaffEntry) {
                         let verses: number = 0;
                         for (const voiceEntry of sourceStaffEntry.VoiceEntries) {
                             verses += Object.keys(voiceEntry.LyricsEntries).length;

+ 214 - 85
src/MusicalScore/ScoreIO/InstrumentReader.ts

@@ -17,14 +17,16 @@ import {RhythmSymbolEnum} from "../VoiceData/Instructions/RhythmInstruction";
 import {KeyEnum} from "../VoiceData/Instructions/KeyInstruction";
 import {IXmlAttribute} from "../../Common/FileIO/Xml";
 import {ChordSymbolContainer} from "../VoiceData/ChordSymbolContainer";
-import * as log from "loglevel";
+import log from "loglevel";
 import {MidiInstrument} from "../VoiceData/Instructions/ClefInstruction";
 import {ChordSymbolReader} from "./MusicSymbolModules/ChordSymbolReader";
 import {ExpressionReader} from "./MusicSymbolModules/ExpressionReader";
 import {RepetitionInstructionReader} from "./MusicSymbolModules/RepetitionInstructionReader";
 import {SlurReader} from "./MusicSymbolModules/SlurReader";
 import {StemDirectionType} from "../VoiceData/VoiceEntry";
-//import Dictionary from "typescript-collections/dist/lib/Dictionary";
+import {NoteType, NoteTypeHandler} from "../VoiceData/NoteType";
+import { SystemLinesEnumHelper } from "../Graphical/SystemLinesEnum";
+// import {Dictionary} from "typescript-collections";
 
 // FIXME: The following classes are missing
 //type ChordSymbolContainer = any;
@@ -77,6 +79,7 @@ export class InstrumentReader {
   private divisions: number = 0;
   private currentMeasure: SourceMeasure;
   private previousMeasure: SourceMeasure;
+  private currentClefNumber: number = 1;
   private currentXmlMeasureIndex: number = 0;
   private currentStaff: Staff;
   private currentStaffEntry: SourceStaffEntry;
@@ -86,7 +89,7 @@ export class InstrumentReader {
   private activeClefsHaveBeenInitialized: boolean[];
   private activeKeyHasBeenInitialized: boolean = false;
   private abstractInstructions: [number, AbstractNotationInstruction][] = [];
-  private openChordSymbolContainer: ChordSymbolContainer;
+  private openChordSymbolContainers: ChordSymbolContainer[] = [];
   private expressionReaders: ExpressionReader[];
   private currentVoiceGenerator: VoiceGenerator;
   //private openSlurDict: { [n: number]: Slur; } = {};
@@ -121,7 +124,7 @@ export class InstrumentReader {
     }
     this.currentMeasure = currentMeasure;
     this.inSourceMeasureInstrumentIndex = this.musicSheet.getGlobalStaffIndexOfFirstStaff(this.instrument);
-    if (this.repetitionInstructionReader !== undefined) {
+    if (this.repetitionInstructionReader) {
      this.repetitionInstructionReader.prepareReadingMeasure(currentMeasure, this.currentXmlMeasureIndex);
     }
     let currentFraction: Fraction = new Fraction(0, 1);
@@ -130,9 +133,26 @@ export class InstrumentReader {
     this.maxTieNoteFraction = new Fraction(0, 1);
     let lastNoteWasGrace: boolean = false;
     try {
-      const xmlMeasureListArr: IXmlElement[] = this.xmlMeasureList[this.currentXmlMeasureIndex].elements();
-      for (const xmlNode of xmlMeasureListArr) {
-        if (xmlNode.name === "note") {
+      const measureNode: IXmlElement = this.xmlMeasureList[this.currentXmlMeasureIndex];
+      const xmlMeasureListArr: IXmlElement[] = measureNode.elements();
+      if (currentMeasure.Rules.UseXMLMeasureNumbers && !Number.isInteger(currentMeasure.MeasureNumberXML)) {
+        const measureNumberXml: number = parseInt(measureNode.attribute("number")?.value, 10);
+        if (Number.isInteger(measureNumberXml)) {
+            currentMeasure.MeasureNumberXML = measureNumberXml;
+        }
+      }
+      for (let xmlNodeIndex: number = 0; xmlNodeIndex < xmlMeasureListArr.length; xmlNodeIndex++) {
+        const xmlNode: IXmlElement = xmlMeasureListArr[xmlNodeIndex];
+        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") {
@@ -143,7 +163,7 @@ export class InstrumentReader {
           }
           let noteStaff: number = 1;
           if (this.instrument.Staves.length > 1) {
-            if (xmlNode.element("staff") !== undefined) {
+            if (xmlNode.element("staff")) {
               noteStaff = parseInt(xmlNode.element("staff").value, 10);
               if (isNaN(noteStaff)) {
                 log.debug("InstrumentReader.readNextXmlMeasure.get staff number");
@@ -154,11 +174,11 @@ export class InstrumentReader {
 
           this.currentStaff = this.instrument.Staves[noteStaff - 1];
           const isChord: boolean = xmlNode.element("chord") !== undefined;
-          if (xmlNode.element("voice") !== undefined) {
+          if (xmlNode.element("voice")) {
             const noteVoice: number = parseInt(xmlNode.element("voice").value, 10);
             this.currentVoiceGenerator = this.getOrCreateVoiceGenerator(noteVoice, noteStaff - 1);
           } else {
-            if (!isChord || this.currentVoiceGenerator === undefined) {
+            if (!isChord || !this.currentVoiceGenerator) {
               this.currentVoiceGenerator = this.getOrCreateVoiceGenerator(1, noteStaff - 1);
             }
           }
@@ -167,7 +187,7 @@ export class InstrumentReader {
           let normalNotes: number = 2;
           let typeDuration: Fraction = undefined;
           let isTuplet: boolean = false;
-          if (xmlNode.element("duration") !== undefined) {
+          if (xmlNode.element("duration")) {
             noteDivisions = parseInt(xmlNode.element("duration").value, 10);
             if (!isNaN(noteDivisions)) {
               noteDuration = new Fraction(noteDivisions, 4 * this.divisions);
@@ -176,11 +196,11 @@ export class InstrumentReader {
               } else {
                 typeDuration = this.getNoteDurationFromTypeNode(xmlNode);
               }
-              if (xmlNode.element("time-modification") !== undefined) {
+              if (xmlNode.element("time-modification")) {
                 noteDuration = this.getNoteDurationForTuplet(xmlNode);
                 const time: IXmlElement = xmlNode.element("time-modification");
-                if (time !== undefined) {
-                  if (time.element("normal-notes") !== undefined) {
+                if (time) {
+                  if (time.element("normal-notes")) {
                     normalNotes = parseInt(time.element("normal-notes").value, 10);
                   }
                 }
@@ -197,6 +217,8 @@ export class InstrumentReader {
           const restNote: boolean = xmlNode.element("rest") !== undefined;
           //log.info("New note found!", noteDivisions, noteDuration.toString(), restNote);
 
+          const notationsNode: IXmlElement = xmlNode.element("notations"); // used for multiple checks further on
+
           const isGraceNote: boolean = xmlNode.element("grace") !== undefined || noteDivisions === 0 || isChord && lastNoteWasGrace;
           let graceNoteSlash: boolean = false;
           let graceSlur: boolean = false;
@@ -214,8 +236,8 @@ export class InstrumentReader {
             noteDuration = this.getNoteDurationFromTypeNode(xmlNode);
 
             const notationNode: IXmlElement = xmlNode.element("notations");
-            if (notationNode !== undefined) {
-              if (notationNode.element("slur") !== undefined) {
+            if (notationNode) {
+              if (notationNode.element("slur")) {
                 graceSlur = true;
                 // grace slurs could be non-binary, but VexFlow.GraceNoteGroup modifier system is currently only boolean for slurs.
               }
@@ -225,25 +247,27 @@ export class InstrumentReader {
           // check for cue note
           let isCueNote: boolean = false;
           const cueNode: IXmlElement = xmlNode.element("cue");
-          if (cueNode !== undefined) {
+          if (cueNode) {
             isCueNote = true;
           }
           // alternative: check for <type size="cue">
           const typeNode: IXmlElement = xmlNode.element("type");
-          if (typeNode !== undefined) {
+          let noteTypeXml: NoteType = NoteType.UNDEFINED;
+          if (typeNode) {
             const sizeAttr: Attr = typeNode.attribute("size");
             if (sizeAttr !== undefined && sizeAttr !== null) {
               if (sizeAttr.value === "cue") {
                 isCueNote = true;
               }
             }
+            noteTypeXml = NoteTypeHandler.StringToNoteType(typeNode.value);
           }
 
           // check stem element
           let stemDirectionXml: StemDirectionType = StemDirectionType.Undefined;
           let stemColorXml: string;
           const stemNode: IXmlElement = xmlNode.element("stem");
-          if (stemNode !== undefined) {
+          if (stemNode) {
             switch (stemNode.value) {
               case "down":
                 stemDirectionXml = StemDirectionType.Down;
@@ -267,6 +291,33 @@ export class InstrumentReader {
             }
           }
 
+          // check Tremolo
+          let tremoloStrokes: number = 0;
+          let vibratoStrokes: boolean = false;
+          if (notationsNode) {
+            const ornamentsNode: IXmlElement = notationsNode.element("ornaments");
+            if (ornamentsNode) {
+              const tremoloNode: IXmlElement = ornamentsNode.element("tremolo");
+              if (tremoloNode) {
+                const tremoloType: Attr = tremoloNode.attribute("type");
+                if (tremoloType && tremoloType.value === "single") {
+                  const tremoloStrokesGiven: number = parseInt(tremoloNode.value, 10);
+                  if (tremoloStrokesGiven > 0) {
+                    tremoloStrokes = tremoloStrokesGiven;
+                  }
+                }
+                // TODO implement type "start". Vexflow doesn't have tremolo beams yet though (shorter than normal beams)
+              }
+              const vibratoNode: IXmlElement = ornamentsNode.element("wavy-line");
+              if (vibratoNode !== undefined) {
+                const vibratoType: Attr = vibratoNode.attribute("type");
+                if (vibratoType && vibratoType.value === "start") {
+                  vibratoStrokes = true;
+                }
+              }
+            }
+          }
+
           // check notehead/color
           let noteheadColorXml: string;
           const noteheadNode: IXmlElement = xmlNode.element("notehead");
@@ -281,10 +332,10 @@ export class InstrumentReader {
           const noteColorAttr: Attr = xmlNode.attribute("color");
           if (noteColorAttr) { // can be undefined
             noteColorXml = this.parseXmlColor(noteColorAttr.value);
-            if (noteheadColorXml === undefined) {
+            if (!noteheadColorXml) {
               noteheadColorXml = noteColorXml;
             }
-            if (stemColorXml === undefined) {
+            if (!stemColorXml) {
               stemColorXml = noteColorXml;
             }
           }
@@ -328,40 +379,42 @@ export class InstrumentReader {
             this.currentStaffEntry.Timestamp.Equals(new Fraction(0, 1)) && !this.currentStaffEntry.hasNotes()
           );
           this.saveAbstractInstructionList(this.instrument.Staves.length, beginOfMeasure);
-          if (this.openChordSymbolContainer !== undefined) {
-            this.currentStaffEntry.ChordContainer = this.openChordSymbolContainer;
-            this.openChordSymbolContainer = undefined;
+          if (this.openChordSymbolContainers.length !== 0) {
+            this.currentStaffEntry.ChordContainers = this.openChordSymbolContainers;
+            // TODO handle multiple chords on one note/staffentry
+            this.openChordSymbolContainers = [];
           }
-          if (this.activeRhythm !== undefined) {
+          if (this.activeRhythm) {
             // (*) this.musicSheet.SheetPlaybackSetting.Rhythm = this.activeRhythm.Rhythm;
           }
           if (!isTuplet && !isGraceNote) {
             noteDuration = new Fraction(noteDivisions, 4 * this.divisions);
           }
           this.currentVoiceGenerator.read(
-            xmlNode, noteDuration, typeDuration, normalNotes, restNote,
+            xmlNode, noteDuration, typeDuration, noteTypeXml, normalNotes, restNote,
             this.currentStaffEntry, this.currentMeasure,
             measureStartAbsoluteTimestamp,
             this.maxTieNoteFraction, isChord, guitarPro,
-            printObject, isCueNote, stemDirectionXml, stemColorXml, noteheadColorXml
+            printObject, isCueNote, isGraceNote, stemDirectionXml, tremoloStrokes, stemColorXml, noteheadColorXml,
+            vibratoStrokes
           );
 
-          const notationsNode: IXmlElement = xmlNode.element("notations");
-          if (notationsNode !== undefined && notationsNode.element("dynamics") !== undefined) {
+          // notationsNode created further up for multiple checks
+          if (notationsNode !== undefined && notationsNode.element("dynamics")) {
             const expressionReader: ExpressionReader = this.expressionReaders[this.readExpressionStaffNumber(xmlNode) - 1];
-            if (expressionReader !== undefined) {
+            if (expressionReader) {
              expressionReader.readExpressionParameters(
                xmlNode, this.instrument, this.divisions, currentFraction, previousFraction, this.currentMeasure.MeasureNumber, false
              );
              expressionReader.read(
                xmlNode, this.currentMeasure, previousFraction
              );
-          }
+            }
           }
           lastNoteWasGrace = isGraceNote;
         } else if (xmlNode.name === "attributes") {
           const divisionsNode: IXmlElement = xmlNode.element("divisions");
-          if (divisionsNode !== undefined) {
+          if (divisionsNode) {
             this.divisions = parseInt(divisionsNode.value, 10);
             if (isNaN(this.divisions)) {
               const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/DivisionError",
@@ -375,10 +428,9 @@ export class InstrumentReader {
                 throw new MusicSheetReadingException(errorMsg + this.instrument.Name);
               }
             }
-
           }
           if (
-            xmlNode.element("divisions") === undefined &&
+            !xmlNode.element("divisions") &&
             this.divisions === 0 &&
             this.currentXmlMeasureIndex === 0
           ) {
@@ -399,6 +451,46 @@ export class InstrumentReader {
           if (this.isAttributesNodeAtEndOfMeasure(this.xmlMeasureList[this.currentXmlMeasureIndex], xmlNode)) {
             this.saveClefInstructionAtEndOfMeasure();
           }
+          const staffDetailsNode: IXmlElement = xmlNode.element("staff-details");
+          if (staffDetailsNode) {
+            const staffLinesNode: IXmlElement = staffDetailsNode.element("staff-lines");
+            if (staffLinesNode) {
+              let staffNumber: number = 1;
+              const staffNumberAttr: Attr = staffDetailsNode.attribute("number");
+              if (staffNumberAttr) {
+                staffNumber = parseInt(staffNumberAttr.value, 10);
+              }
+              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));
@@ -416,11 +508,11 @@ export class InstrumentReader {
           const directionTypeNode: IXmlElement = xmlNode.element("direction-type");
           // (*) MetronomeReader.readMetronomeInstructions(xmlNode, this.musicSheet, this.currentXmlMeasureIndex);
           let relativePositionInMeasure: number = Math.min(1, currentFraction.RealValue);
-          if (this.activeRhythm !== undefined && this.activeRhythm.Rhythm !== undefined) {
+          if (this.activeRhythm !== undefined && this.activeRhythm.Rhythm) {
             relativePositionInMeasure /= this.activeRhythm.Rhythm.RealValue;
           }
           let handeled: boolean = false;
-          if (this.repetitionInstructionReader !== undefined) {
+          if (this.repetitionInstructionReader) {
             handeled = this.repetitionInstructionReader.handleRepetitionInstructionsFromWordsOrSymbols( directionTypeNode,
                                                                                                         relativePositionInMeasure);
           }
@@ -430,8 +522,8 @@ export class InstrumentReader {
            if (staffIndex < this.expressionReaders.length) {
              expressionReader = this.expressionReaders[staffIndex];
            }
-           if (expressionReader !== undefined) {
-             if (directionTypeNode.element("octave-shift") !== undefined) {
+           if (expressionReader) {
+             if (directionTypeNode.element("octave-shift")) {
                expressionReader.readExpressionParameters(
                  xmlNode, this.instrument, this.divisions, currentFraction, previousFraction, this.currentMeasure.MeasureNumber, true
                );
@@ -444,18 +536,42 @@ export class InstrumentReader {
            }
           }
         } else if (xmlNode.name === "barline") {
-          if (this.repetitionInstructionReader !== undefined) {
-           const measureEndsSystem: boolean = false;
-           this.repetitionInstructionReader.handleLineRepetitionInstructions(xmlNode, measureEndsSystem);
+          if (this.repetitionInstructionReader) {
+           const measureEndsSystem: boolean = this.repetitionInstructionReader.handleLineRepetitionInstructions(xmlNode);
            if (measureEndsSystem) {
-             this.currentMeasure.BreakSystemAfter = true;
-             this.currentMeasure.endsPiece = true;
+             this.currentMeasure.HasEndLine = true;
            }
           }
+          const location: IXmlAttribute = xmlNode.attribute("location");
+          const isEndingBarline: boolean = (xmlNodeIndex === xmlMeasureListArr.length - 1);
+          if (isEndingBarline || (location && location.value === "right")) {
+            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") {
           // (*) MetronomeReader.readTempoInstruction(xmlNode, this.musicSheet, this.currentXmlMeasureIndex);
+          try {
+            if (xmlNode.hasAttributes && xmlNode.attribute("tempo") !== undefined) {
+
+                const tempo: number = parseFloat(xmlNode.attribute("tempo").value);
+
+                // should set the PlaybackSettings only at first Measure
+                if (this.currentXmlMeasureIndex === 0) {
+                    this.musicSheet.DefaultStartTempoInBpm = tempo;
+                    this.musicSheet.HasBPMInfo = true;
+                }
+            }
+          } catch (e) {
+            log.debug("InstrumentReader.readTempoInstruction", e);
+          }
         } else if (xmlNode.name === "harmony") {
-                    this.openChordSymbolContainer = ChordSymbolReader.readChordSymbol(xmlNode, this.musicSheet, this.activeKey);
+          // new chord, could be second chord on same staffentry/note
+          this.openChordSymbolContainers.push(ChordSymbolReader.readChordSymbol(xmlNode, this.musicSheet, this.activeKey));
         }
       }
       for (const j in this.voiceGeneratorsDict) {
@@ -476,11 +592,19 @@ export class InstrumentReader {
 
         for (let i: number = 0; i < this.expressionReaders.length; i++) {
          const reader: ExpressionReader = this.expressionReaders[i];
-         if (reader !== undefined) {
+         if (reader) {
            reader.checkForOpenExpressions(this.currentMeasure, currentFraction);
-      }
+          }
         }
       }
+
+      // if this is the first measure and no BPM info found, we set it to 120
+      // next measures will automatically inherit that value
+      if (!this.musicSheet.HasBPMInfo) {
+        this.currentMeasure.TempoInBPM = 120;
+      } else if (currentMeasure.TempoInBPM === 0 && this.previousMeasure) {
+        this.currentMeasure.TempoInBPM = this.previousMeasure.TempoInBPM;
+      }
     } catch (e) {
       if (divisionsException) {
         throw new MusicSheetReadingException(e.Message);
@@ -529,13 +653,13 @@ export class InstrumentReader {
   private getOrCreateVoiceGenerator(voiceId: number, staffId: number): VoiceGenerator {
     const staff: Staff = this.instrument.Staves[staffId];
     let voiceGenerator: VoiceGenerator = this.voiceGeneratorsDict[voiceId];
-    if (voiceGenerator !== undefined) {
+    if (voiceGenerator) {
       if (staff.Voices.indexOf(voiceGenerator.GetVoice) === -1) {
         staff.Voices.push(voiceGenerator.GetVoice);
       }
     } else {
       const mainVoiceGenerator: VoiceGenerator = this.staffMainVoiceGeneratorDict[staffId];
-      if (mainVoiceGenerator !== undefined) {
+      if (mainVoiceGenerator) {
         voiceGenerator = new VoiceGenerator(this.instrument, voiceId, this.slurReader, mainVoiceGenerator.GetVoice);
         staff.Voices.push(voiceGenerator.GetVoice);
         this.voiceGeneratorsDict[voiceId] = voiceGenerator;
@@ -570,7 +694,7 @@ export class InstrumentReader {
     }
     const clefInstruction: ClefInstruction = new ClefInstruction(ClefEnum.G, 0, 2);
     let firstStaffEntry: SourceStaffEntry;
-    if (first.FirstInstructionsStaffEntries[staffIndex] === undefined) {
+    if (!first.FirstInstructionsStaffEntries[staffIndex]) {
       firstStaffEntry = new SourceStaffEntry(undefined, undefined);
       first.FirstInstructionsStaffEntries[staffIndex] = firstStaffEntry;
     } else {
@@ -593,7 +717,7 @@ export class InstrumentReader {
     }
     const keyInstruction: KeyInstruction = new KeyInstruction(undefined, 0, KeyEnum.major);
     for (let j: number = this.inSourceMeasureInstrumentIndex; j < this.inSourceMeasureInstrumentIndex + this.instrument.Staves.length; j++) {
-      if (first.FirstInstructionsStaffEntries[j] === undefined) {
+      if (!first.FirstInstructionsStaffEntries[j]) {
         const firstStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
         first.FirstInstructionsStaffEntries[j] = firstStaffEntry;
         keyInstruction.Parent = firstStaffEntry;
@@ -640,7 +764,7 @@ export class InstrumentReader {
    * @returns {boolean}
    */
   private isAttributesNodeAtEndOfMeasure(parentNode: IXmlElement, attributesNode: IXmlElement): boolean {
-    const childs: IXmlElement[] = parentNode.elements().slice();
+    const childs: IXmlElement[] = parentNode.elements().slice(); // slice=arrayCopy
     let attributesNodeIndex: number = 0;
     for (let i: number = 0; i < childs.length; i++) {
       if (childs[i] === attributesNode) {
@@ -665,9 +789,9 @@ export class InstrumentReader {
    */
   private getNoteDurationFromTypeNode(xmlNode: IXmlElement): Fraction {
     const typeNode: IXmlElement = xmlNode.element("type");
-    if (typeNode !== undefined) {
+    if (typeNode) {
       const type: string = typeNode.value;
-      return this.currentVoiceGenerator.getNoteDurationFromType(type);
+      return NoteTypeHandler.getNoteDurationFromType(type);
     }
     return new Fraction(0, 4 * this.divisions);
   }
@@ -678,15 +802,15 @@ export class InstrumentReader {
    * @param guitarPro
    */
   private addAbstractInstruction(node: IXmlElement, guitarPro: boolean): void {
-    if (node.element("divisions") !== undefined) {
+    if (node.element("divisions")) {
       if (node.elements().length === 1) {
         return;
       }
     }
     const transposeNode: IXmlElement = node.element("transpose");
-    if (transposeNode !== undefined) {
+    if (transposeNode) {
       const chromaticNode: IXmlElement = transposeNode.element("chromatic");
-      if (chromaticNode !== undefined) {
+      if (chromaticNode) {
         this.instrument.PlaybackTranspose = parseInt(chromaticNode.value, 10);
       }
     }
@@ -700,7 +824,7 @@ export class InstrumentReader {
         let staffNumber: number = 1;
         let clefOctaveOffset: number = 0;
         const lineNode: IXmlElement = nodeList.element("line");
-        if (lineNode !== undefined) {
+        if (lineNode) {
           try {
             line = parseInt(lineNode.value, 10);
           } catch (ex) {
@@ -715,13 +839,10 @@ export class InstrumentReader {
 
         }
         const signNode: IXmlElement = nodeList.element("sign");
-        if (signNode !== undefined) {
+        if (signNode) {
           try {
             clefEnum = ClefEnum[signNode.value];
             if (!ClefInstruction.isSupportedClef(clefEnum)) {
-              if (clefEnum === ClefEnum.TAB && guitarPro) {
-                clefOctaveOffset = -1;
-              }
               errorMsg = ITextTranslation.translateText(
                 "ReaderErrorMessages/ClefError",
                 "Unsupported clef found -> using default clef."
@@ -730,6 +851,9 @@ export class InstrumentReader {
               clefEnum = ClefEnum.G;
               line = 2;
             }
+            if (clefEnum === ClefEnum.TAB) {
+              clefOctaveOffset = -1;
+            }
           } catch (e) {
             errorMsg = ITextTranslation.translateText(
               "ReaderErrorMessages/ClefError",
@@ -743,7 +867,7 @@ export class InstrumentReader {
 
         }
         const clefOctaveNode: IXmlElement = nodeList.element("clef-octave-change");
-        if (clefOctaveNode !== undefined) {
+        if (clefOctaveNode) {
           try {
             clefOctaveOffset = parseInt(clefOctaveNode.value, 10);
           } catch (e) {
@@ -759,6 +883,10 @@ export class InstrumentReader {
         if (nodeList.hasAttributes && nodeList.attributes()[0].name === "number") {
           try {
             staffNumber = parseInt(nodeList.attributes()[0].value, 10);
+            if (staffNumber > this.currentClefNumber) {
+              staffNumber = this.currentClefNumber;
+            }
+            this.currentClefNumber = staffNumber + 1;
           } catch (err) {
             errorMsg = ITextTranslation.translateText(
               "ReaderErrorMessages/ClefError",
@@ -766,6 +894,7 @@ export class InstrumentReader {
             );
             this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
             staffNumber = 1;
+            this.currentClefNumber = staffNumber + 1;
           }
         }
 
@@ -776,7 +905,7 @@ export class InstrumentReader {
     if (node.element("key") !== undefined && this.instrument.MidiInstrumentId !== MidiInstrument.Percussion) {
       let key: number = 0;
       const keyNode: IXmlElement = node.element("key").element("fifths");
-      if (keyNode !== undefined) {
+      if (keyNode) {
         try {
           key = parseInt(keyNode.value, 10);
         } catch (ex) {
@@ -792,10 +921,10 @@ export class InstrumentReader {
       }
       let keyEnum: KeyEnum = KeyEnum.none;
       let modeNode: IXmlElement = node.element("key");
-      if (modeNode !== undefined) {
+      if (modeNode) {
         modeNode = modeNode.element("mode");
       }
-      if (modeNode !== undefined) {
+      if (modeNode) {
         try {
           keyEnum = KeyEnum[modeNode.value];
         } catch (ex) {
@@ -811,7 +940,7 @@ export class InstrumentReader {
       const keyInstruction: KeyInstruction = new KeyInstruction(undefined, key, keyEnum);
       this.abstractInstructions.push([1, keyInstruction]);
     }
-    if (node.element("time") !== undefined) {
+    if (node.element("time")) {
       const timeNode: IXmlElement = node.element("time");
       let symbolEnum: RhythmSymbolEnum = RhythmSymbolEnum.NONE;
       let timePrintObject: boolean = true;
@@ -835,7 +964,7 @@ export class InstrumentReader {
 
       let num: number = 0;
       let denom: number = 0;
-      const senzaMisura: boolean = (timeNode !== undefined && timeNode.element("senza-misura") !== undefined);
+      const senzaMisura: boolean = (timeNode && timeNode.element("senza-misura") !== undefined);
       const timeList: IXmlElement[] = node.elements("time");
       const beatsList: IXmlElement[] = [];
       const typeList: IXmlElement[] = [];
@@ -920,12 +1049,12 @@ export class InstrumentReader {
             this.abstractInstructions.splice(i, 1);
           } else if (beginOfMeasure) {
             let firstStaffEntry: SourceStaffEntry;
-            if (this.currentMeasure !== undefined) {
+            if (this.currentMeasure) {
               const newClefInstruction: ClefInstruction = clefInstruction;
               const sseIndex: number = this.inSourceMeasureInstrumentIndex + key - 1;
               const firstSse: SourceStaffEntry = this.currentMeasure.FirstInstructionsStaffEntries[sseIndex];
               if (this.currentXmlMeasureIndex === 0) {
-                if (firstSse === undefined) {
+                if (!firstSse) {
                   firstStaffEntry = new SourceStaffEntry(undefined, undefined);
                   this.currentMeasure.FirstInstructionsStaffEntries[sseIndex] = firstStaffEntry;
                   newClefInstruction.Parent = firstStaffEntry;
@@ -947,7 +1076,7 @@ export class InstrumentReader {
                 }
               } else if (!this.activeClefsHaveBeenInitialized[key - 1]) {
                 const first: SourceMeasure = this.musicSheet.SourceMeasures[0];
-                if (first.FirstInstructionsStaffEntries[sseIndex] === undefined) {
+                if (!first.FirstInstructionsStaffEntries[sseIndex]) {
                   firstStaffEntry = new SourceStaffEntry(undefined, undefined);
                 } else {
                   firstStaffEntry = first.FirstInstructionsStaffEntries[sseIndex];
@@ -972,7 +1101,7 @@ export class InstrumentReader {
       }
       if (value instanceof KeyInstruction) {
         const keyInstruction: KeyInstruction = <KeyInstruction>value;
-        if (this.activeKey === undefined || this.activeKey.Key !== keyInstruction.Key) {
+        if (!this.activeKey || this.activeKey.Key !== keyInstruction.Key) {
           this.activeKey = keyInstruction;
           this.abstractInstructions.splice(i, 1);
           let sourceMeasure: SourceMeasure;
@@ -986,10 +1115,10 @@ export class InstrumentReader {
           } else {
             sourceMeasure = this.currentMeasure;
           }
-          if (sourceMeasure !== undefined) {
+          if (sourceMeasure) {
             for (let j: number = this.inSourceMeasureInstrumentIndex; j < this.inSourceMeasureInstrumentIndex + numberOfStaves; j++) {
               const newKeyInstruction: KeyInstruction = keyInstruction;
-              if (sourceMeasure.FirstInstructionsStaffEntries[j] === undefined) {
+              if (!sourceMeasure.FirstInstructionsStaffEntries[j]) {
                 const firstStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
                 sourceMeasure.FirstInstructionsStaffEntries[j] = firstStaffEntry;
                 newKeyInstruction.Parent = firstStaffEntry;
@@ -1016,14 +1145,14 @@ export class InstrumentReader {
       }
       if (value instanceof RhythmInstruction) {
         const rhythmInstruction: RhythmInstruction = <RhythmInstruction>value;
-        if (this.activeRhythm === undefined || this.activeRhythm !== rhythmInstruction) {
+        if (!this.activeRhythm || this.activeRhythm !== rhythmInstruction) {
           this.activeRhythm = rhythmInstruction;
           this.abstractInstructions.splice(i, 1);
-          if (this.currentMeasure !== undefined) {
+          if (this.currentMeasure) {
             for (let j: number = this.inSourceMeasureInstrumentIndex; j < this.inSourceMeasureInstrumentIndex + numberOfStaves; j++) {
               const newRhythmInstruction: RhythmInstruction = rhythmInstruction;
               let firstStaffEntry: SourceStaffEntry;
-              if (this.currentMeasure.FirstInstructionsStaffEntries[j] === undefined) {
+              if (!this.currentMeasure.FirstInstructionsStaffEntries[j]) {
                 firstStaffEntry = new SourceStaffEntry(undefined, undefined);
                 this.currentMeasure.FirstInstructionsStaffEntries[j] = firstStaffEntry;
               } else {
@@ -1051,7 +1180,7 @@ export class InstrumentReader {
       if (value instanceof ClefInstruction) {
         const clefInstruction: ClefInstruction = <ClefInstruction>value;
         if (
-          (this.activeClefs[key - 1] === undefined) ||
+          (!this.activeClefs[key - 1]) ||
           (clefInstruction.ClefType !== this.activeClefs[key - 1].ClefType || (
             clefInstruction.ClefType === this.activeClefs[key - 1].ClefType &&
             clefInstruction.Line !== this.activeClefs[key - 1].Line
@@ -1076,13 +1205,13 @@ export class InstrumentReader {
   private getNoteDurationForTuplet(xmlNode: IXmlElement): Fraction {
     let duration: Fraction = new Fraction(0, 1);
     const typeDuration: Fraction = this.getNoteDurationFromTypeNode(xmlNode);
-    if (xmlNode.element("time-modification") !== undefined) {
+    if (xmlNode.element("time-modification")) {
       const time: IXmlElement = xmlNode.element("time-modification");
-      if (time !== undefined) {
-        if (time.element("actual-notes") !== undefined && time.element("normal-notes") !== undefined) {
+      if (time) {
+        if (time.element("actual-notes") !== undefined && time.element("normal-notes")) {
           const actualNotes: IXmlElement = time.element("actual-notes");
           const normalNotes: IXmlElement = time.element("normal-notes");
-          if (actualNotes !== undefined && normalNotes !== undefined) {
+          if (actualNotes !== undefined && normalNotes) {
             const actual: number = parseInt(actualNotes.value, 10);
             const normal: number = parseInt(normalNotes.value, 10);
             duration = new Fraction(normal * typeDuration.Numerator, actual * typeDuration.Denominator);
@@ -1095,9 +1224,9 @@ export class InstrumentReader {
 
   private readExpressionStaffNumber(xmlNode: IXmlElement): number {
    let directionStaffNumber: number = 1;
-   if (xmlNode.element("staff") !== undefined) {
+   if (xmlNode.element("staff")) {
      const staffNode: IXmlElement = xmlNode.element("staff");
-     if (staffNode !== undefined) {
+     if (staffNode) {
        try {
          directionStaffNumber = parseInt(staffNode.value, 10);
        } catch (ex) {
@@ -1130,10 +1259,10 @@ export class InstrumentReader {
       const xmlMeasureListArr: IXmlElement[] = this.xmlMeasureList[xmlMeasureIndex].elements();
       for (let idx: number = 0, len: number = xmlMeasureListArr.length; idx < len; ++idx) {
         const xmlNode: IXmlElement = xmlMeasureListArr[idx];
-        if (xmlNode.name === "note" && xmlNode.element("time-modification") === undefined) {
+        if (xmlNode.name === "note" && !xmlNode.element("time-modification")) {
           const durationNode: IXmlElement = xmlNode.element("duration");
           const typeNode: IXmlElement = xmlNode.element("type");
-          if (durationNode !== undefined && typeNode !== undefined) {
+          if (durationNode !== undefined && typeNode) {
             const type: string = typeNode.value;
             let noteDuration: number = 0;
             try {

+ 78 - 62
src/MusicalScore/ScoreIO/MusicSheetReader.ts

@@ -6,7 +6,7 @@ import {IXmlElement} from "../../Common/FileIO/Xml";
 import {Instrument} from "../Instrument";
 import {ITextTranslation} from "../Interfaces/ITextTranslation";
 import {MusicSheetReadingException} from "../Exceptions";
-import * as log from "loglevel";
+import log from "loglevel";
 import {IXmlAttribute} from "../../Common/FileIO/Xml";
 import {RhythmInstruction} from "../VoiceData/Instructions/RhythmInstruction";
 import {RhythmSymbolEnum} from "../VoiceData/Instructions/RhythmInstruction";
@@ -21,17 +21,19 @@ import {MusicSymbolModuleFactory} from "./MusicSymbolModuleFactory";
 import {IAfterSheetReadingModule} from "../Interfaces/IAfterSheetReadingModule";
 import {RepetitionInstructionReader} from "./MusicSymbolModules/RepetitionInstructionReader";
 import {RepetitionCalculator} from "./MusicSymbolModules/RepetitionCalculator";
+import {EngravingRules} from "../Graphical/EngravingRules";
 
 export class MusicSheetReader /*implements IMusicSheetReader*/ {
 
-    constructor(afterSheetReadingModules: IAfterSheetReadingModule[] = undefined) {
-     if (afterSheetReadingModules === undefined) {
+    constructor(afterSheetReadingModules: IAfterSheetReadingModule[] = undefined, rules: EngravingRules = new EngravingRules()) {
+     if (!afterSheetReadingModules) {
        this.afterSheetReadingModules = [];
      } else {
        this.afterSheetReadingModules = afterSheetReadingModules;
      }
      this.repetitionInstructionReader = MusicSymbolModuleFactory.createRepetitionInstructionReader();
      this.repetitionCalculator = MusicSymbolModuleFactory.createRepetitionCalculator();
+     this.rules = rules;
     }
 
     private repetitionInstructionReader: RepetitionInstructionReader;
@@ -42,6 +44,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
     private currentMeasure: SourceMeasure;
     private previousMeasure: SourceMeasure;
     private currentFraction: Fraction;
+    public rules: EngravingRules;
 
     public get CompleteNumberOfStaves(): number {
         return this.completeNumberOfStaves;
@@ -63,7 +66,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
         try {
             return this._createMusicSheet(root, path);
         } catch (e) {
-            log.info("MusicSheetReader.CreateMusicSheet", e);
+            log.error("MusicSheetReader.CreateMusicSheet", e);
             return undefined;
         }
     }
@@ -89,15 +92,15 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
     //}
     //public ReadMusicSheetParameters(sheetObject: MusicSheetParameterObject, root: IXmlElement, path: string): MusicSheetParameterObject {
     //  this.musicSheet = new MusicSheet();
-    //  if (root !== undefined) {
+    //  if (root) {
     //    this.pushSheetLabels(root, path);
-    //    if (this.musicSheet.Title !== undefined) {
+    //    if (this.musicSheet.Title) {
     //      sheetObject.Title = this.musicSheet.Title.text;
     //    }
-    //    if (this.musicSheet.Composer !== undefined) {
+    //    if (this.musicSheet.Composer) {
     //      sheetObject.Composer = this.musicSheet.Composer.text;
     //    }
-    //    if (this.musicSheet.Lyricist !== undefined) {
+    //    if (this.musicSheet.Lyricist) {
     //      sheetObject.Lyricist = this.musicSheet.Lyricist.text;
     //    }
     //    let partlistNode: IXmlElement = root.element("part-list");
@@ -116,12 +119,13 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
         let sourceMeasureCounter: number = 0;
         this.musicSheet = new MusicSheet();
         this.musicSheet.Path = path;
-        if (root === undefined) {
+        this.musicSheet.Rules = this.rules;
+        if (!root) {
             throw new MusicSheetReadingException("Undefined root element");
         }
         this.pushSheetLabels(root, path);
         const partlistNode: IXmlElement = root.element("part-list");
-        if (partlistNode === undefined) {
+        if (!partlistNode) {
             throw new MusicSheetReadingException("Undefined partListNode");
         }
 
@@ -132,10 +136,10 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
         this.currentFraction = new Fraction(0, 1);
         let guitarPro: boolean = false;
         let encoding: IXmlElement = root.element("identification");
-        if (encoding !== undefined) {
+        if (encoding) {
             encoding = encoding.element("encoding");
         }
-        if (encoding !== undefined) {
+        if (encoding) {
             encoding = encoding.element("software");
         }
         if (encoding !== undefined && encoding.value === "Guitar Pro 5") {
@@ -143,10 +147,11 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
         }
 
         while (couldReadMeasure) {
-            if (this.currentMeasure !== undefined && this.currentMeasure.endsPiece) {
+            // TODO changing this.rules.PartAndSystemAfterFinalBarline requires a reload of the piece for measure numbers to be updated
+            if (this.currentMeasure !== undefined && this.currentMeasure.HasEndLine && this.rules.NewPartAndSystemAfterFinalBarline) {
                 sourceMeasureCounter = 0;
             }
-            this.currentMeasure = new SourceMeasure(this.completeNumberOfStaves);
+            this.currentMeasure = new SourceMeasure(this.completeNumberOfStaves, this.musicSheet.Rules);
             for (const instrumentReader of instrumentReaders) {
                 try {
                     couldReadMeasure = couldReadMeasure && instrumentReader.readNextXmlMeasure(this.currentMeasure, this.currentFraction, guitarPro);
@@ -169,9 +174,9 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
             }
         }
 
-        if (this.repetitionInstructionReader !== undefined) {
+        if (this.repetitionInstructionReader) {
             this.repetitionInstructionReader.removeRedundantInstructions();
-            if (this.repetitionCalculator !== undefined) {
+            if (this.repetitionCalculator) {
                 this.repetitionCalculator.calculateRepetitions(this.musicSheet, this.repetitionInstructionReader.repetitionInstructions);
             }
         }
@@ -183,6 +188,9 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
          afterSheetReadingModule.calculate(this.musicSheet);
         }
 
+        //this.musicSheet.DefaultStartTempoInBpm = this.musicSheet.SourceMeasures[0].TempoInBPM;
+        this.musicSheet.userStartTempoInBPM = this.musicSheet.userStartTempoInBPM || this.musicSheet.DefaultStartTempoInBpm;
+
         return this.musicSheet;
     }
 
@@ -215,7 +223,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
 
                 currentInstrument.createStaves(instrumentNumberOfStaves);
                 instrumentReaders.push(new InstrumentReader(this.repetitionInstructionReader, xmlMeasureList, currentInstrument));
-                if (this.repetitionInstructionReader !== undefined) {
+                if (this.repetitionInstructionReader) {
                     this.repetitionInstructionReader.xmlMeasureList[counter] = xmlMeasureList;
                 }
                 counter++;
@@ -233,7 +241,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
     private checkIfRhythmInstructionsAreSetAndEqual(instrumentReaders: InstrumentReader[]): void {
         const rhythmInstructions: RhythmInstruction[] = [];
         for (let i: number = 0; i < this.completeNumberOfStaves; i++) {
-            if (this.currentMeasure.FirstInstructionsStaffEntries[i] !== undefined) {
+            if (this.currentMeasure.FirstInstructionsStaffEntries[i]) {
                 const last: AbstractNotationInstruction = this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions[
                 this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions.length - 1
                     ];
@@ -264,7 +272,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                     this.currentMeasure.FirstInstructionsStaffEntries[i].removeAllInstructionsOfTypeRhythmInstruction();
                     this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions.push(rhythmInstruction.clone());
                 }
-                if (this.currentMeasure.FirstInstructionsStaffEntries[i] === undefined) {
+                if (!this.currentMeasure.FirstInstructionsStaffEntries[i]) {
                     this.currentMeasure.FirstInstructionsStaffEntries[i] = new SourceStaffEntry(undefined, undefined);
                     this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions.push(rhythmInstruction.clone());
                 }
@@ -277,7 +285,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
         if (rhythmInstructions.length === 0 && this.currentMeasure === this.musicSheet.SourceMeasures[0]) {
             const rhythmInstruction: RhythmInstruction = new RhythmInstruction(new Fraction(4, 4, 0, false), RhythmSymbolEnum.NONE);
             for (let i: number = 0; i < this.completeNumberOfStaves; i++) {
-                if (this.currentMeasure.FirstInstructionsStaffEntries[i] === undefined) {
+                if (!this.currentMeasure.FirstInstructionsStaffEntries[i]) {
                     this.currentMeasure.FirstInstructionsStaffEntries[i] = new SourceStaffEntry(undefined, undefined);
                 } else {
                     this.currentMeasure.FirstInstructionsStaffEntries[i].removeAllInstructionsOfTypeRhythmInstruction();
@@ -363,7 +371,8 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
         if (!this.currentMeasure.ImplicitMeasure) {
             sourceMeasureCounter++;
         }
-        this.currentMeasure.Duration = maxInstrumentDuration;
+        this.currentMeasure.Duration = maxInstrumentDuration; // can be 1/1 in a 4/4 time signature
+        this.currentMeasure.ActiveTimeSignature = activeRhythm;
         this.currentMeasure.MeasureNumber = sourceMeasureCounter;
         for (let i: number = 0; i < instrumentsDurations.length; i++) {
             const instrumentsDuration: Fraction = instrumentsDurations[i];
@@ -407,10 +416,10 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
      * @returns {boolean}
      */
     private checkIfMeasureIsImplicit(maxInstrumentDuration: Fraction, activeRhythm: Fraction): boolean {
-        if (this.previousMeasure === undefined && maxInstrumentDuration.lt(activeRhythm)) {
+        if (!this.previousMeasure && maxInstrumentDuration.lt(activeRhythm)) {
             return true;
         }
-        if (this.previousMeasure !== undefined) {
+        if (this.previousMeasure) {
             return Fraction.plus(this.previousMeasure.Duration, maxInstrumentDuration).Equals(activeRhythm);
         }
         return false;
@@ -436,7 +445,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
     private graphicalMeasureIsEmpty(index: number): boolean {
         let counter: number = 0;
         for (let i: number = 0; i < this.currentMeasure.VerticalSourceStaffEntryContainers.length; i++) {
-            if (this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries[index] === undefined) {
+            if (!this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries[index]) {
                 counter++;
             }
         }
@@ -451,7 +460,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
         for (let i: number = this.currentMeasure.VerticalSourceStaffEntryContainers.length - 1; i >= 0; i--) {
             for (let j: number = this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries.length - 1; j >= 0; j--) {
                 const sourceStaffEntry: SourceStaffEntry = this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries[j];
-                if (sourceStaffEntry !== undefined) {
+                if (sourceStaffEntry) {
                     for (let k: number = sourceStaffEntry.VoiceEntries.length - 1; k >= 0; k--) {
                         const voiceEntry: VoiceEntry = sourceStaffEntry.VoiceEntries[k];
                         if (voiceEntry.Notes.length === 0) {
@@ -469,7 +478,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
             let counter: number = 0;
             for (let idx: number = 0, len: number = this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries.length; idx < len; ++idx) {
                 const sourceStaffEntry: SourceStaffEntry = this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries[idx];
-                if (sourceStaffEntry === undefined) {
+                if (!sourceStaffEntry) {
                     counter++;
                 }
             }
@@ -488,14 +497,14 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
         this.readComposer(root);
         this.readTitle(root);
         try {
-            if (this.musicSheet.Title === undefined || this.musicSheet.Composer === undefined) {
+            if (!this.musicSheet.Title || !this.musicSheet.Composer) {
                 this.readTitleAndComposerFromCredits(root); // this can also throw an error
             }
         } catch (ex) {
             log.info("MusicSheetReader.pushSheetLabels", "readTitleAndComposerFromCredits", ex);
         }
         try {
-            if (this.musicSheet.Title === undefined) {
+            if (!this.musicSheet.Title) {
                 const barI: number = Math.max(
                     0, filePath.lastIndexOf("/"), filePath.lastIndexOf("\\")
                 );
@@ -520,7 +529,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
 
     private readComposer(root: IXmlElement): void {
         const identificationNode: IXmlElement = root.element("identification");
-        if (identificationNode !== undefined) {
+        if (identificationNode) {
             const creators: IXmlElement[] = identificationNode.elements("creator");
             for (let idx: number = 0, len: number = creators.length; idx < len; ++idx) {
                 const creator: IXmlElement = creators[idx];
@@ -555,16 +564,17 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
             }
             if (credit.attribute("page").value === "1") {
                 let creditChild: IXmlElement = undefined;
-                if (credit !== undefined) {
+                if (credit) {
                     creditChild = credit.element("credit-words");
                     if (!creditChild.attribute("justify")) {
                         break;
                     }
-                    const creditJustify: string = creditChild.attribute("justify").value;
-                    const creditY: string = creditChild.attribute("default-y").value;
-                    const creditYInfo: number = parseFloat(creditY);
-                    if (creditYInfo > systemYCoordinates) {
-                        if (this.musicSheet.Title === undefined) {
+                    const creditJustify: string = creditChild.attribute("justify")?.value;
+                    const creditY: string = creditChild.attribute("default-y")?.value;
+                    const creditYGiven: boolean = creditY !== undefined && creditY !== null;
+                    const creditYInfo: number = creditYGiven ? parseFloat(creditY) : Number.MIN_VALUE;
+                    if (creditYGiven && creditYInfo > systemYCoordinates) {
+                        if (!this.musicSheet.Title) {
                             const creditSize: string = creditChild.attribute("font-size").value;
                             const titleCreditSizeInt: number = parseFloat(creditSize);
                             if (largestTitleCreditSize < titleCreditSizeInt) {
@@ -572,7 +582,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                                 finalTitle = creditChild.value;
                             }
                         }
-                        if (this.musicSheet.Subtitle === undefined) {
+                        if (!this.musicSheet.Subtitle) {
                             if (creditJustify !== "right" && creditJustify !== "left") {
                                 if (largestCreditYInfo < creditYInfo) {
                                     largestCreditYInfo = creditYInfo;
@@ -585,7 +595,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                                 }
                             }
                         }
-                        if (!(this.musicSheet.Composer !== undefined && this.musicSheet.Lyricist !== undefined)) {
+                        if (!(this.musicSheet.Composer !== undefined && this.musicSheet.Lyricist)) {
                             switch (creditJustify) {
                                 case "right":
                                     this.musicSheet.Composer = new Label(this.trimString(creditChild.value));
@@ -601,16 +611,16 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                 }
             }
         }
-        if (this.musicSheet.Title === undefined && finalTitle) {
+        if (!this.musicSheet.Title && finalTitle) {
             this.musicSheet.Title = new Label(this.trimString(finalTitle));
         }
-        if (this.musicSheet.Subtitle === undefined && finalSubtitle) {
+        if (!this.musicSheet.Subtitle && finalSubtitle) {
             this.musicSheet.Subtitle = new Label(this.trimString(finalSubtitle));
         }
     }
 
     private computeSystemYCoordinates(root: IXmlElement): number {
-        if (root.element("defaults") === undefined) {
+        if (!root.element("defaults")) {
             return 0;
         }
         let paperHeight: number = 0;
@@ -628,11 +638,11 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
             const measures: IXmlElement[] = parts[idx].elements("measure");
             for (let idx2: number = 0, len2: number = measures.length; idx2 < len2; ++idx2) {
                 const measure: IXmlElement = measures[idx2];
-                if (measure.element("print") !== undefined) {
+                if (measure.element("print")) {
                     const systemLayouts: IXmlElement[] = measure.element("print").elements("system-layout");
                     for (let idx3: number = 0, len3: number = systemLayouts.length; idx3 < len3; ++idx3) {
                         const syslab: IXmlElement = systemLayouts[idx3];
-                        if (syslab.element("top-system-distance") !== undefined) {
+                        if (syslab.element("top-system-distance")) {
                             const topSystemDistanceString: string = syslab.element("top-system-distance").value;
                             topSystemDistance = parseFloat(topSystemDistanceString);
                             found = true;
@@ -646,9 +656,9 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                 break;
             }
         }
-        if (root.element("defaults").element("system-layout") !== undefined) {
+        if (root.element("defaults").element("system-layout")) {
             const syslay: IXmlElement = root.element("defaults").element("system-layout");
-            if (syslay.element("top-system-distance") !== undefined) {
+            if (syslay.element("top-system-distance")) {
                 const topSystemDistanceString: string = root.element("defaults").element("system-layout").element("top-system-distance").value;
                 topSystemDistance = parseFloat(topSystemDistanceString);
             }
@@ -662,27 +672,27 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
     private readTitle(root: IXmlElement): void {
         const titleNode: IXmlElement = root.element("work");
         let titleNodeChild: IXmlElement = undefined;
-        if (titleNode !== undefined) {
+        if (titleNode) {
             titleNodeChild = titleNode.element("work-title");
-            if (titleNodeChild !== undefined && titleNodeChild.value) {
+            if (titleNodeChild && titleNodeChild.value) {
                 this.musicSheet.Title = new Label(this.trimString(titleNodeChild.value));
             }
         }
         const movementNode: IXmlElement = root.element("movement-title");
         let finalSubTitle: string = "";
-        if (movementNode !== undefined) {
-            if (this.musicSheet.Title === undefined) {
+        if (movementNode) {
+            if (!this.musicSheet.Title) {
                 this.musicSheet.Title = new Label(this.trimString(movementNode.value));
             } else {
                 finalSubTitle = this.trimString(movementNode.value);
             }
         }
-        if (titleNode !== undefined) {
+        if (titleNode) {
             const subtitleNodeChild: IXmlElement = titleNode.element("work-number");
-            if (subtitleNodeChild !== undefined) {
+            if (subtitleNodeChild) {
                 const workNumber: string = subtitleNodeChild.value;
                 if (workNumber) {
-                    if (finalSubTitle) {
+                    if (finalSubTitle === "") {
                         finalSubTitle = workNumber;
                     } else {
                         finalSubTitle = finalSubTitle + ", " + workNumber;
@@ -719,12 +729,18 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                         try {
                             if (partElement.name === "part-name") {
                                 instrument.Name = partElement.value;
+                                if (partElement.attribute("print-object") &&
+                                   partElement.attribute("print-object").value === "no") {
+                                    instrument.NameLabel.print = false;
+                                }
+                            } else if (partElement.name === "part-abbreviation") {
+                                instrument.PartAbbreviation = partElement.value;
                             } else if (partElement.name === "score-instrument") {
                                 const subInstrument: SubInstrument = new SubInstrument(instrument);
                                 subInstrument.idString = partElement.firstAttribute.value;
                                 instrument.SubInstruments.push(subInstrument);
                                 const subElement: IXmlElement = partElement.element("instrument-name");
-                                if (subElement !== undefined) {
+                                if (subElement) {
                                     subInstrument.name = subElement.value;
                                     subInstrument.setMidiInstrument(subElement.value);
                                 }
@@ -784,7 +800,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                         instrument.SubInstruments.push(subInstrument);
                     }
                     instrumentDict[instrIdString] = instrument;
-                    if (currentGroup !== undefined) {
+                    if (currentGroup) {
                         currentGroup.InstrumentalGroups.push(instrument);
                         this.musicSheet.Instruments.push(instrument);
                     } else {
@@ -794,7 +810,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                 } else {
                     if ((node.name === "part-group") && (node.attribute("type").value === "start")) {
                         const iG: InstrumentalGroup = new InstrumentalGroup("group", this.musicSheet, currentGroup);
-                        if (currentGroup !== undefined) {
+                        if (currentGroup) {
                             currentGroup.InstrumentalGroups.push(iG);
                         } else {
                             this.musicSheet.InstrumentalGroups.push(iG);
@@ -802,10 +818,10 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                         currentGroup = iG;
                     } else {
                         if ((node.name === "part-group") && (node.attribute("type").value === "stop")) {
-                            if (currentGroup !== undefined) {
+                            if (currentGroup) {
                                 if (currentGroup.InstrumentalGroups.length === 1) {
                                     const instr: InstrumentalGroup = currentGroup.InstrumentalGroups[0];
-                                    if (currentGroup.Parent !== undefined) {
+                                    if (currentGroup.Parent) {
                                         currentGroup.Parent.InstrumentalGroups.push(instr);
                                         this._removeFromArray(currentGroup.Parent.InstrumentalGroups, currentGroup);
                                     } else {
@@ -846,12 +862,12 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
             const xmlMeasureList: IXmlElement[] = partNode.elements("measure");
             if (xmlMeasureList.length > 0) {
                 const xmlMeasure: IXmlElement = xmlMeasureList[0];
-                if (xmlMeasure !== undefined) {
+                if (xmlMeasure) {
                     let stavesNode: IXmlElement = xmlMeasure.element("attributes");
-                    if (stavesNode !== undefined) {
+                    if (stavesNode) {
                         stavesNode = stavesNode.element("staves");
                     }
-                    if (stavesNode === undefined) {
+                    if (!stavesNode) {
                         num++;
                     } else {
                         num += parseInt(stavesNode.value, 10);
@@ -876,13 +892,13 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
     private getInstrumentNumberOfStavesFromXml(partNode: IXmlElement): number {
         let num: number = 0;
         const xmlMeasure: IXmlElement = partNode.element("measure");
-        if (xmlMeasure !== undefined) {
+        if (xmlMeasure) {
             const attributes: IXmlElement = xmlMeasure.element("attributes");
             let staves: IXmlElement = undefined;
-            if (attributes !== undefined) {
+            if (attributes) {
                 staves = attributes.element("staves");
             }
-            if (attributes === undefined || staves === undefined) {
+            if (!attributes || !staves) {
                 num = 1;
             } else {
                 num = parseInt(staves.value, 10);

+ 62 - 24
src/MusicalScore/ScoreIO/MusicSymbolModules/ArticulationReader.ts

@@ -1,10 +1,12 @@
 import {ArticulationEnum, VoiceEntry} from "../../VoiceData/VoiceEntry";
 import {IXmlAttribute, IXmlElement} from "../../../Common/FileIO/Xml";
-import * as log from "loglevel";
+import log from "loglevel";
 import {TechnicalInstruction, TechnicalInstructionType} from "../../VoiceData/Instructions/TechnicalInstruction";
 import {OrnamentContainer, OrnamentEnum} from "../../VoiceData/OrnamentContainer";
 import {PlacementEnum} from "../../VoiceData/Expressions/AbstractExpression";
 import {AccidentalEnum} from "../../../Common/DataObjects/Pitch";
+import { Articulation } from "../../VoiceData/Articulation";
+import { Note } from "../../VoiceData/Note";
 export class ArticulationReader {
 
   private getAccEnumFromString(input: string): AccidentalEnum {
@@ -41,26 +43,39 @@ export class ArticulationReader {
    */
   public addArticulationExpression(node: IXmlElement, currentVoiceEntry: VoiceEntry): void {
     if (node !== undefined && node.elements().length > 0) {
-      const childNotes: IXmlElement[] = node.elements();
-      for (let idx: number = 0, len: number = childNotes.length; idx < len; ++idx) {
-        const childNote: IXmlElement = childNotes[idx];
-        const name: string = childNote.name;
+      const childNodes: IXmlElement[] = node.elements();
+      for (let idx: number = 0, len: number = childNodes.length; idx < len; ++idx) {
+        const childNode: IXmlElement = childNodes[idx];
+        let name: string = childNode.name;
         try {
           // some Articulations appear in Xml separated with a "-" (eg strong-accent), we remove it for enum parsing
-          name.replace("-", "");
+          name = name.replace("-", "");
           const articulationEnum: ArticulationEnum = ArticulationEnum[name];
           if (VoiceEntry.isSupportedArticulation(articulationEnum)) {
-            // staccato should be first
+            let placement: PlacementEnum = PlacementEnum.Above;
+            if (childNode.attribute("placement")?.value === "below") {
+              placement = PlacementEnum.Below;
+            }
+            const newArticulation: Articulation = new Articulation(articulationEnum, placement);
+            // staccato should be first // necessary?
             if (name === "staccato") {
               if (currentVoiceEntry.Articulations.length > 0 &&
-                currentVoiceEntry.Articulations[0] !== ArticulationEnum.staccato) {
-                currentVoiceEntry.Articulations.splice(0, 0, articulationEnum);
+                currentVoiceEntry.Articulations[0].articulationEnum !== ArticulationEnum.staccato) {
+                currentVoiceEntry.Articulations.splice(0, 0, newArticulation); // TODO can't this overwrite another articulation?
+              }
+            }
+            if (name === "strongaccent") { // see name.replace("-", "") above
+              const marcatoType: string = childNode?.attribute("type")?.value;
+              if (marcatoType === "up") {
+                newArticulation.articulationEnum = ArticulationEnum.marcatoup;
+              } else if (marcatoType === "down") {
+                newArticulation.articulationEnum = ArticulationEnum.marcatodown;
               }
             }
 
             // don't add the same articulation twice
-            if (currentVoiceEntry.Articulations.indexOf(articulationEnum) === -1) {
-              currentVoiceEntry.Articulations.push(articulationEnum);
+            if (!currentVoiceEntry.hasArticulation(newArticulation)) {
+              currentVoiceEntry.Articulations.push(newArticulation);
             }
           }
         } catch (ex) {
@@ -80,13 +95,17 @@ export class ArticulationReader {
   public addFermata(xmlNode: IXmlElement, currentVoiceEntry: VoiceEntry): void {
     // fermata appears as separate tag in XML
     let articulationEnum: ArticulationEnum = ArticulationEnum.fermata;
-    if (xmlNode.attributes().length > 0 && xmlNode.attribute("type") !== undefined) {
+    if (xmlNode.attributes().length > 0 && xmlNode.attribute("type")) {
       if (xmlNode.attribute("type").value === "inverted") {
         articulationEnum = ArticulationEnum.invertedfermata;
       }
     }
+    let placement: PlacementEnum = PlacementEnum.Above;
+    if (xmlNode.attribute("placement")?.value === "below") {
+      placement = PlacementEnum.Below;
+    }
     // add to VoiceEntry
-    currentVoiceEntry.Articulations.push(articulationEnum);
+    currentVoiceEntry.Articulations.push(new Articulation(articulationEnum, placement));
   }
 
   /**
@@ -94,11 +113,12 @@ export class ArticulationReader {
    * @param xmlNode
    * @param currentVoiceEntry
    */
-  public addTechnicalArticulations(xmlNode: IXmlElement, currentVoiceEntry: VoiceEntry): void {
+  public addTechnicalArticulations(xmlNode: IXmlElement, currentVoiceEntry: VoiceEntry, currentNote: Note): void {
     interface XMLElementToArticulationEnum {
       [xmlElement: string]: ArticulationEnum;
     }
     const xmlElementToArticulationEnum: XMLElementToArticulationEnum = {
+      "bend": ArticulationEnum.bend,
       "down-bow": ArticulationEnum.downbow,
       "open-string": ArticulationEnum.naturalharmonic,
       "snap-pizzicato": ArticulationEnum.snappizzicato,
@@ -113,15 +133,23 @@ export class ArticulationReader {
       }
       const articulationEnum: ArticulationEnum = xmlElementToArticulationEnum[xmlArticulation];
       const node: IXmlElement = xmlNode.element(xmlArticulation);
-      if (node !== undefined) {
-        if (currentVoiceEntry.Articulations.indexOf(articulationEnum) === -1) {
-          currentVoiceEntry.Articulations.push(articulationEnum);
+      if (node) {
+        let placement: PlacementEnum; // set undefined by default, to not restrict placement
+        if (node.attribute("placement")?.value === "above") {
+          placement = PlacementEnum.Above;
+        }
+        if (node.attribute("placement")?.value === "below") {
+          placement = PlacementEnum.Below;
+        }
+        const newArticulation: Articulation = new Articulation(articulationEnum, placement);
+        if (!currentVoiceEntry.hasArticulation(newArticulation)) {
+          currentVoiceEntry.Articulations.push(newArticulation);
         }
       }
     }
 
     const nodeFingering: IXmlElement = xmlNode.element("fingering");
-    if (nodeFingering !== undefined) {
+    if (nodeFingering) {
       const currentTechnicalInstruction: TechnicalInstruction = new TechnicalInstruction();
       currentTechnicalInstruction.type = TechnicalInstructionType.Fingering;
       currentTechnicalInstruction.value = nodeFingering.value;
@@ -145,6 +173,8 @@ export class ArticulationReader {
             currentTechnicalInstruction.placement = PlacementEnum.NotYetDefined;
         }
       }
+      currentTechnicalInstruction.sourceNote = currentNote;
+      currentNote.Fingering = currentTechnicalInstruction;
       currentVoiceEntry.TechnicalInstructions.push(currentTechnicalInstruction);
     }
   }
@@ -155,7 +185,7 @@ export class ArticulationReader {
    * @param currentVoiceEntry
    */
   public addOrnament(ornamentsNode: IXmlElement, currentVoiceEntry: VoiceEntry): void {
-    if (ornamentsNode !== undefined) {
+    if (ornamentsNode) {
       let ornament: OrnamentContainer = undefined;
 
       interface XMLElementToOrnamentEnum {
@@ -177,13 +207,20 @@ export class ArticulationReader {
           continue;
         }
         const node: IXmlElement = ornamentsNode.element(ornamentElement);
-        if (node !== undefined) {
+        if (node) {
           ornament = new OrnamentContainer(elementToOrnamentEnum[ornamentElement]);
+          const placementAttr: Attr = node.attribute("placement");
+          if (placementAttr) {
+            const placementString: string = placementAttr.value;
+            if (placementString === "below") {
+              ornament.placement = PlacementEnum.Below;
+            }
+          }
         }
       }
-      if (ornament !== undefined) {
+      if (ornament) {
         const accidentalsList: IXmlElement[] = ornamentsNode.elements("accidental-mark");
-        if (accidentalsList !== undefined) {
+        if (accidentalsList) {
           let placement: PlacementEnum = PlacementEnum.Below;
           let accidental: AccidentalEnum = AccidentalEnum.NONE;
           const accidentalsListArr: IXmlElement[] = accidentalsList;
@@ -192,7 +229,7 @@ export class ArticulationReader {
             let text: string = accidentalNode.value;
             accidental = this.getAccEnumFromString(text);
             const placementAttr: IXmlAttribute = accidentalNode.attribute("placement");
-            if (accidentalNode.hasAttributes && placementAttr !== undefined) {
+            if (accidentalNode.hasAttributes && placementAttr) {
               text = placementAttr.value;
               if (text === "above") {
                 placement = PlacementEnum.Above;
@@ -211,5 +248,6 @@ export class ArticulationReader {
         currentVoiceEntry.OrnamentContainer = ornament;
       }
     }
-  }
+  } // /addOrnament
+
 }

+ 24 - 12
src/MusicalScore/ScoreIO/MusicSymbolModules/ChordSymbolReader.ts

@@ -1,18 +1,19 @@
-import {IXmlElement} from "../../../Common/FileIO/Xml";
+import {IXmlElement, IXmlAttribute} from "../../../Common/FileIO/Xml";
 import {MusicSheet} from "../../MusicSheet";
 import {ChordDegreeText, ChordSymbolContainer, ChordSymbolEnum, Degree} from "../../VoiceData/ChordSymbolContainer";
 import {AccidentalEnum, NoteEnum, Pitch} from "../../../Common/DataObjects/Pitch";
 import {KeyInstruction} from "../../VoiceData/Instructions/KeyInstruction";
 import {ITextTranslation} from "../../Interfaces/ITextTranslation";
-import * as log from "loglevel";
+import log from "loglevel";
 
 export class ChordSymbolReader {
     public static readChordSymbol(xmlNode: IXmlElement, musicSheet: MusicSheet, activeKey: KeyInstruction): ChordSymbolContainer {
         const root: IXmlElement = xmlNode.element("root");
         const kind: IXmlElement = xmlNode.element("kind");
+        const kindText: IXmlAttribute = kind.attribute("text");
 
         // must be always present
-        if (root === undefined || kind === undefined) {
+        if (!root || !kind) {
           return undefined;
         }
 
@@ -20,7 +21,7 @@ export class ChordSymbolReader {
         const rootAlter: IXmlElement = root.element("root-alter");
 
         // a valid NoteEnum value should be present
-        if (rootStep === undefined) {
+        if (!rootStep) {
             return undefined;
         }
         let rootNote: NoteEnum;
@@ -36,7 +37,7 @@ export class ChordSymbolReader {
 
         // an alteration value isn't necessary
         let rootAlteration: AccidentalEnum = AccidentalEnum.NONE;
-        if (rootAlter !== undefined) {
+        if (rootAlter) {
             try {
                 rootAlteration = Pitch.AccidentalFromHalfTones(parseInt(rootAlter.value, undefined));
             } catch (ex) {
@@ -49,7 +50,18 @@ export class ChordSymbolReader {
         }
         // using default octave value, to be changed later
         const rootPitch: Pitch = new Pitch(rootNote, 1, rootAlteration);
-        const kindValue: string = kind.value.trim().replace("-", "");
+        let kindValue: string = kind.value.trim().replace("-", "");
+        if (kindText) {
+            switch (kindText.value) {
+                case "aug":
+                    kindValue = "augmented";
+                    break;
+                case "dim":
+                    kindValue = "diminished";
+                    break;
+                default:
+            }
+        }
         let chordKind: ChordSymbolEnum;
         try {
             chordKind = ChordSymbolEnum[kindValue];
@@ -64,11 +76,11 @@ export class ChordSymbolReader {
         // bass is optional
         let bassPitch: Pitch = undefined;
         const bass: IXmlElement = xmlNode.element("bass");
-        if (bass !== undefined) {
+        if (bass) {
             const bassStep: IXmlElement = bass.element("bass-step");
             const bassAlter: IXmlElement = bass.element("bass-alter");
             let bassNote: NoteEnum = NoteEnum.C;
-            if (bassStep !== undefined) {
+            if (bassStep) {
                 try {
                     bassNote = NoteEnum[bassStep.value.trim()];
                 } catch (ex) {
@@ -80,7 +92,7 @@ export class ChordSymbolReader {
                 }
             }
             let bassAlteration: AccidentalEnum = AccidentalEnum.NONE;
-            if (bassAlter !== undefined) {
+            if (bassAlter) {
                 try {
                     bassAlteration = Pitch.AccidentalFromHalfTones(parseInt(bassAlter.value, undefined));
                 } catch (ex) {
@@ -96,11 +108,11 @@ export class ChordSymbolReader {
         // degree is optional
         let degree: Degree = undefined;
         const degreeNode: IXmlElement = xmlNode.element("degree");
-        if (degreeNode !== undefined) {
+        if (degreeNode) {
             const degreeValue: IXmlElement = degreeNode.element("degree-value");
             const degreeAlter: IXmlElement = degreeNode.element("degree-alter");
             const degreeType: IXmlElement = degreeNode.element("degree-type");
-            if (degreeValue === undefined || degreeAlter === undefined || degreeType === undefined) {
+            if (!degreeValue || !degreeAlter || !degreeType) {
               return undefined;
             }
 
@@ -139,6 +151,6 @@ export class ChordSymbolReader {
 
             degree = new Degree(value, alter, text);
         }
-        return new ChordSymbolContainer(rootPitch, chordKind, bassPitch, degree, activeKey);
+        return new ChordSymbolContainer(rootPitch, chordKind, bassPitch, degree, musicSheet.Rules);
     }
 }

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.