diff --git a/Changes b/Changes index 95e8f33a..1a8e7e69 100644 --- a/Changes +++ b/Changes @@ -1,3 +1,63 @@ +6.040 2023-12-26 + + - !Highlights + + - Images can be [placed everywhere](https://chordpro.org/chordpro/directives-image/). + They can be placed relative to the paper, the page, the column, + and the lyrics. + + - Images can be [embedded[(https://chordpro.org/chordpro/directives-image/#inline-images) in text (lyrics) lines, either as part of + the text similar to a glyph, or somewhere else on the page + relative to a particular place in the text. The latter is most + interesting for annotations. + + - Delegates are images too. Annotate your lyrics with SVG images, + or with musical notes using ABC or Lilypond. + + - Chord and keyboard diagrams are images too. And you can use + string and keyboard diagrams simultaneously. + + - Resources like configs, tasks and images are now more logically + searched using [resource + libraries](https://chordpro.org/chordpro/resources/). + + - !ChordPro functionality + - Rework paths handling for consistent resource handling; + eliminate App::Packager. + - Inline images. + - {image} can have label and align properties. + - {define}d chords overrules suppress list. + - (ABC) Use QuickJS XS (JavaScript::QuickJS) as preferred. + - (ABC) ABC embedding no longer uses nodeJS (npx). + - (ABC) Make split work (and enable by default). + - (ABC) Images are left aligned by default. + - (Lilypond) Images are left aligned by default. + - Improve runtime info. + - Suppress songs that do not have content. + - Suppress table of content entry for a song w/ {ns toc=no}. + - (PDF) Image scale strategy change for spread images. + - (PDF/Writer) Add generalized add_object for objects and images. + - (PDF) Prevent case problems when looking up fonts for SVG. + - (PDF) Add aliases for web standard fonts like serif, sans, ... + - (PDF) Ignore leading empty and ignores (was: leading empty only). + - (Windows) ChordPro now installs as a 64-bit application in + \Program Files instead of \Program Files (x86). + You are adsvised to remove the old 32bits install first. + - !Bug fixes + - Prevent warning when parsing {key} and trancode to nashville/roman. + - Fix chord inversion (issue 321). + - Fix comment lines disturbing a consecutive series of {chord}s. + - Fix typo in Wx Preferencesdialog, causing it to crash. + - Fix problem with PDF/SVG caching fonts. + - Fix comment labels for delegates (issue 329.3). + - (Wx) Filter configs on prp ans json. + - Fix memorize/recall/transpose issue 333. + - Fix issue 334. + - !Internal + - (ABC) ABC embedding use tohtml instead of toxhtml. + - (PDF) Enhance assets (wip), labels; move grid to separate module. + - Experimental ##include facility. + 6.030 2023-09-18 - !Highlights diff --git a/GNUmakefile b/GNUmakefile index 5d525f24..bb5f0899 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -11,6 +11,10 @@ all : Makefile cleanup test : Makefile env PERL5LIB=$(shell pwd)/CPAN $(MAKE) -f Makefile test +.PHONY : tests +tests : test + prove -b xt + .PHONY : clean clean : cleanup rm -f *~ @@ -47,15 +51,13 @@ to_tmp_cpan : rsync ${RSYNC_ARGS} --files-from=MANIFEST.CPAN ./ ${TMP_DST}/ to_c : + test -d /mnt/c/Users || mount /mnt/c ${MAKE} to_tmp to_tmp_cpan TMP_DST=/mnt/c${W10DIR} - rsync ${RSYNC_ARGS} --files-from=MANIFEST.ABC ./ /mnt/c${W10DIR}/ - rm -fr /mnt/c${W10DIR}/pp/macos to_mac : resources rsync ${RSYNC_ARGS} --files-from=MANIFEST ./ ${MACDST}/ rsync ${RSYNC_ARGS} --files-from=MANIFEST.WX ./ ${MACDST}/ rsync ${RSYNC_ARGS} --files-from=MANIFEST.CPAN ./ ${MACDST}/ - rsync ${RSYNC_ARGS} --files-from=MANIFEST.ABC ./ ${MACDST}/ ssh macky rm -fr ${MACDST}/pp/windows release : @@ -99,7 +101,7 @@ checkjson : do \ json_pp -json_opt relaxed < $$i > .json/`basename $$i`; \ done - rm -f .json/pd_colour.json + cd .json; rm keyboard.json dark.json resetchords.json ${JSONVALIDATOR} ${JSONOPTS} \ ${CFGLIB}/config.schema .json/*.json rm -fr .json @@ -112,7 +114,6 @@ WW := w10 wkit : _wkit1 _wkit _wkit2 _wkit : - test -d /mnt/c/Users || mount /mnt/c ${MAKE} to_c ssh ${WW} gmake -C Chordpro/pp/windows scp ${WW}:Chordpro/pp/windows/ChordPro-Installer\*.exe ${HOME}/tmp/ @@ -121,16 +122,10 @@ _wkit1 : -VBoxManage startvm ${VM} --type headless _wkit2 : + sudo umount /misc/c VBoxManage controlvm ${VM} poweroff VBoxManage snapshot ${VM} restorecurrent -abckit :: - npm update --silent abc2svg - tar zcvf pp/windows/abc2svg_qjs.tar \ - -C ${HOME}/node_modules \ - --exclude "**/Scc1t?/*" \ - abc2svg - .PHONY: TAGS TAGS: diff --git a/MANIFEST b/MANIFEST index 7db69e97..b68f1685 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,4 +1,3 @@ - Changes LICENSE MANIFEST @@ -15,6 +14,8 @@ lib/ChordPro/Config.pm lib/ChordPro/Config/Properties.pm lib/ChordPro/Delegate/ABC.pm lib/ChordPro/Delegate/Lilypond.pm +lib/ChordPro/Delegate/SVG.pm +lib/ChordPro/Dumper.pm lib/ChordPro/Output/ChordPro.pm lib/ChordPro/Output/Common.pm lib/ChordPro/Output/Debug.pm @@ -23,10 +24,12 @@ lib/ChordPro/Output/LaTeX.pm lib/ChordPro/Output/MMA.pm lib/ChordPro/Output/Markdown.pm lib/ChordPro/Output/PDF.pm -lib/ChordPro/Output/PDF/KeyboardDiagrams.pm -lib/ChordPro/Output/PDF/StringDiagrams.pm +lib/ChordPro/Output/PDF/Grid.pm +lib/ChordPro/Output/PDF/KeyboardDiagram.pm +lib/ChordPro/Output/PDF/StringDiagram.pm lib/ChordPro/Output/PDF/Writer.pm lib/ChordPro/Output/Text.pm +lib/ChordPro/Paths.pm lib/ChordPro/Song.pm lib/ChordPro/Songbook.pm lib/ChordPro/Testing.pm @@ -63,6 +66,47 @@ lib/ChordPro/lib/SVGPDF/Text.pm lib/ChordPro/lib/SVGPDF/Tspan.pm lib/ChordPro/lib/SVGPDF/Use.pm lib/ChordPro/res/abc/chordproabc.js +lib/ChordPro/res/abc/cmd.js +lib/ChordPro/res/abc/abc2svg/abc2svg-1.js +lib/ChordPro/res/abc/abc2svg/abc2svg.ttf +lib/ChordPro/res/abc/abc2svg/abcdoc-1.js +lib/ChordPro/res/abc/abc2svg/abcemb1-1.js +lib/ChordPro/res/abc/abc2svg/abcemb-1.js +lib/ChordPro/res/abc/abc2svg/abcemb2-1.js +lib/ChordPro/res/abc/abc2svg/ambitus-1.js +lib/ChordPro/res/abc/abc2svg/break-1.js +lib/ChordPro/res/abc/abc2svg/capo-1.js +lib/ChordPro/res/abc/abc2svg/chord-1.js +lib/ChordPro/res/abc/abc2svg/chordnames-1.js +lib/ChordPro/res/abc/abc2svg/clair-1.js +lib/ChordPro/res/abc/abc2svg/clip-1.js +lib/ChordPro/res/abc/abc2svg/cmdline.js +lib/ChordPro/res/abc/abc2svg/combine-1.js +lib/ChordPro/res/abc/abc2svg/COPYING +lib/ChordPro/res/abc/abc2svg/COPYING.LESSER +lib/ChordPro/res/abc/abc2svg/diag-1.js +lib/ChordPro/res/abc/abc2svg/equalbars-1.js +lib/ChordPro/res/abc/abc2svg/err-en.js +lib/ChordPro/res/abc/abc2svg/grid-1.js +lib/ChordPro/res/abc/abc2svg/grid2-1.js +lib/ChordPro/res/abc/abc2svg/grid3-1.js +lib/ChordPro/res/abc/abc2svg/jazzchord-1.js +lib/ChordPro/res/abc/abc2svg/jianpu-1.js +lib/ChordPro/res/abc/abc2svg/json-1.js +lib/ChordPro/res/abc/abc2svg/mdnn-1.js +lib/ChordPro/res/abc/abc2svg/MIDI-1.js +lib/ChordPro/res/abc/abc2svg/nns-1.js +lib/ChordPro/res/abc/abc2svg/page-1.js +lib/ChordPro/res/abc/abc2svg/pedline-1.js +lib/ChordPro/res/abc/abc2svg/perc-1.js +lib/ChordPro/res/abc/abc2svg/psvg-1.js +lib/ChordPro/res/abc/abc2svg/README.md +lib/ChordPro/res/abc/abc2svg/roman-1.js +lib/ChordPro/res/abc/abc2svg/soloffs-1.js +lib/ChordPro/res/abc/abc2svg/sth-1.js +lib/ChordPro/res/abc/abc2svg/strtab-1.js +lib/ChordPro/res/abc/abc2svg/temper-1.js +lib/ChordPro/res/abc/abc2svg/tohtml.js lib/ChordPro/res/config/brandtroemer.json lib/ChordPro/res/config/chordii.json lib/ChordPro/res/config/chordpro.json @@ -140,7 +184,6 @@ pp/windows/chordpro.pp pp/windows/chordpro.rc pp/windows/chordproinst.bmp pp/windows/infoafter.txt -pp/windows/infoafter.txt pp/windows/innosetup.iss pp/windows/innosetup_par.iss pp/windows/ppl.c @@ -155,6 +198,7 @@ t/01_prereq.t t/02_load.t t/100_basic.t t/101_empty.t +t/101_directives.t t/102_new_song.t t/103_title.t t/104_subtitles.t @@ -178,9 +222,12 @@ t/118_tab.t t/119_verse.t t/120_meta.t t/122_memorize.t +t/123_memtrans.t +t/124_memtrans.t t/130_image.jpg t/130_image.t t/131_image.t +t/132_image.t t/140_chords.t t/141_chords.t t/142_chords.t diff --git a/MANIFEST.ABC b/MANIFEST.ABC deleted file mode 100644 index fc2ce798..00000000 --- a/MANIFEST.ABC +++ /dev/null @@ -1,3 +0,0 @@ -pp/windows/qjs.exe -pp/macos/qjs -pp/common/abc2svg_qjs.tar.gz diff --git a/MANIFEST.CPAN b/MANIFEST.CPAN index ec6fd4cb..d04aa24a 100644 --- a/MANIFEST.CPAN +++ b/MANIFEST.CPAN @@ -1,19 +1,16 @@ -pp/windows/abcm2ps.exe -CPAN/App/Packager.pm -CPAN/Capture/Tiny.pm -CPAN/Data/Properties.pm CPAN/File/HomeDir.pm CPAN/File/HomeDir/Darwin.pm +CPAN/File/HomeDir/Darwin/Carbon.pm +CPAN/File/HomeDir/Darwin/Cocoa.pm CPAN/File/HomeDir/Driver.pm CPAN/File/HomeDir/FreeDesktop.pm CPAN/File/HomeDir/MacOS9.pm CPAN/File/HomeDir/Test.pm CPAN/File/HomeDir/Unix.pm CPAN/File/HomeDir/Windows.pm -CPAN/File/HomeDir/Darwin/Carbon.pm -CPAN/File/HomeDir/Darwin/Cocoa.pm CPAN/File/LoadLines.pm CPAN/File/Which.pm +CPAN/Font/TTF.pm CPAN/Font/TTF/AATKern.pm CPAN/Font/TTF/AATutils.pm CPAN/Font/TTF/Anchor.pm @@ -22,8 +19,8 @@ CPAN/Font/TTF/Changes_old.txt CPAN/Font/TTF/Cmap.pm CPAN/Font/TTF/Coverage.pm CPAN/Font/TTF/Cvt_.pm -CPAN/Font/TTF/Delta.pm CPAN/Font/TTF/DSIG.pm +CPAN/Font/TTF/Delta.pm CPAN/Font/TTF/Dumper.pm CPAN/Font/TTF/EBDT.pm CPAN/Font/TTF/EBLC.pm @@ -36,46 +33,45 @@ CPAN/Font/TTF/Fmtx.pm CPAN/Font/TTF/Font.pm CPAN/Font/TTF/Fpgm.pm CPAN/Font/TTF/GDEF.pm +CPAN/Font/TTF/GPOS.pm +CPAN/Font/TTF/GSUB.pm CPAN/Font/TTF/Glat.pm CPAN/Font/TTF/Gloc.pm CPAN/Font/TTF/Glyf.pm CPAN/Font/TTF/Glyph.pm -CPAN/Font/TTF/GPOS.pm CPAN/Font/TTF/GrFeat.pm -CPAN/Font/TTF/GSUB.pm CPAN/Font/TTF/Hdmx.pm CPAN/Font/TTF/Head.pm CPAN/Font/TTF/Hhea.pm CPAN/Font/TTF/Hmtx.pm +CPAN/Font/TTF/Kern.pm CPAN/Font/TTF/Kern/ClassArray.pm CPAN/Font/TTF/Kern/CompactClassArray.pm CPAN/Font/TTF/Kern/OrderedList.pm -CPAN/Font/TTF/Kern.pm CPAN/Font/TTF/Kern/StateTable.pm CPAN/Font/TTF/Kern/Subtable.pm -CPAN/Font/TTF/Loca.pm CPAN/Font/TTF/LTSH.pm +CPAN/Font/TTF/Loca.pm CPAN/Font/TTF/Manual.pod CPAN/Font/TTF/Maxp.pm +CPAN/Font/TTF/Mort.pm CPAN/Font/TTF/Mort/Chain.pm CPAN/Font/TTF/Mort/Contextual.pm CPAN/Font/TTF/Mort/Insertion.pm CPAN/Font/TTF/Mort/Ligature.pm CPAN/Font/TTF/Mort/Noncontextual.pm -CPAN/Font/TTF/Mort.pm CPAN/Font/TTF/Mort/Rearrangement.pm CPAN/Font/TTF/Mort/Subtable.pm CPAN/Font/TTF/Name.pm -CPAN/Font/TTF/OldCmap.pm -CPAN/Font/TTF/OldMort.pm CPAN/Font/TTF/OS_2.pm CPAN/Font/TTF/OTTags.pm +CPAN/Font/TTF/OldCmap.pm +CPAN/Font/TTF/OldMort.pm CPAN/Font/TTF/PCLT.pm -CPAN/Font/TTF.pm +CPAN/Font/TTF/PSNames.pm CPAN/Font/TTF/Post.pm CPAN/Font/TTF/Prep.pm CPAN/Font/TTF/Prop.pm -CPAN/Font/TTF/PSNames.pm CPAN/Font/TTF/Segarr.pm CPAN/Font/TTF/Silf.pm CPAN/Font/TTF/Sill.pm @@ -86,8 +82,8 @@ CPAN/Font/TTF/Useall.pm CPAN/Font/TTF/Utils.pm CPAN/Font/TTF/Vhea.pm CPAN/Font/TTF/Vmtx.pm -CPAN/Font/TTF/Woff/MetaData.pm CPAN/Font/TTF/Woff.pm +CPAN/Font/TTF/Woff/MetaData.pm CPAN/Font/TTF/Woff/PrivateData.pm CPAN/Font/TTF/XMLparse.pm CPAN/IO/String.pm @@ -95,18 +91,19 @@ CPAN/Image/Info.pm CPAN/Image/Info/GIF.pm CPAN/Image/Info/JPEG.pm CPAN/Image/Info/PNG.pm -CPAN/JSON/PP/Boolean.pm CPAN/JSON/PP.pm +CPAN/JSON/PP/Boolean.pm +CPAN/PDF/API2.pm CPAN/PDF/API2/Annotation.pm CPAN/PDF/API2/Basic/PDF/Array.pm CPAN/PDF/API2/Basic/PDF/Bool.pm CPAN/PDF/API2/Basic/PDF/Dict.pm CPAN/PDF/API2/Basic/PDF/File.pm +CPAN/PDF/API2/Basic/PDF/Filter.pm CPAN/PDF/API2/Basic/PDF/Filter/ASCII85Decode.pm CPAN/PDF/API2/Basic/PDF/Filter/ASCIIHexDecode.pm CPAN/PDF/API2/Basic/PDF/Filter/FlateDecode.pm CPAN/PDF/API2/Basic/PDF/Filter/LZWDecode.pm -CPAN/PDF/API2/Basic/PDF/Filter.pm CPAN/PDF/API2/Basic/PDF/Filter/RunLengthDecode.pm CPAN/PDF/API2/Basic/PDF/Literal.pm CPAN/PDF/API2/Basic/PDF/Name.pm @@ -125,95 +122,124 @@ CPAN/PDF/API2/NamedDestination.pm CPAN/PDF/API2/Outline.pm CPAN/PDF/API2/Outlines.pm CPAN/PDF/API2/Page.pm -CPAN/PDF/API2.pm +CPAN/PDF/API2/Resource.pm CPAN/PDF/API2/Resource/BaseFont.pm +CPAN/PDF/API2/Resource/CIDFont.pm +CPAN/PDF/API2/Resource/CIDFont/CJKFont.pm CPAN/PDF/API2/Resource/CIDFont/CJKFont/adobemingstdlightacro.data CPAN/PDF/API2/Resource/CIDFont/CJKFont/adobemyungjostdmediumacro.data CPAN/PDF/API2/Resource/CIDFont/CJKFont/adobesongstdlightacro.data CPAN/PDF/API2/Resource/CIDFont/CJKFont/kozgopromediumacro.data CPAN/PDF/API2/Resource/CIDFont/CJKFont/kozminproregularacro.data -CPAN/PDF/API2/Resource/CIDFont/CJKFont.pm CPAN/PDF/API2/Resource/CIDFont/CMap/japanese.cmap CPAN/PDF/API2/Resource/CIDFont/CMap/korean.cmap CPAN/PDF/API2/Resource/CIDFont/CMap/simplified.cmap CPAN/PDF/API2/Resource/CIDFont/CMap/traditional.cmap -CPAN/PDF/API2/Resource/CIDFont.pm -CPAN/PDF/API2/Resource/CIDFont/TrueType/FontFile.pm CPAN/PDF/API2/Resource/CIDFont/TrueType.pm +CPAN/PDF/API2/Resource/CIDFont/TrueType/FontFile.pm +CPAN/PDF/API2/Resource/ColorSpace.pm CPAN/PDF/API2/Resource/ColorSpace/DeviceN.pm +CPAN/PDF/API2/Resource/ColorSpace/Indexed.pm CPAN/PDF/API2/Resource/ColorSpace/Indexed/ACTFile.pm CPAN/PDF/API2/Resource/ColorSpace/Indexed/Hue.pm -CPAN/PDF/API2/Resource/ColorSpace/Indexed.pm CPAN/PDF/API2/Resource/ColorSpace/Indexed/WebColor.pm -CPAN/PDF/API2/Resource/ColorSpace.pm CPAN/PDF/API2/Resource/ColorSpace/Separation.pm CPAN/PDF/API2/Resource/Colors.pm CPAN/PDF/API2/Resource/ExtGState.pm +CPAN/PDF/API2/Resource/Font.pm CPAN/PDF/API2/Resource/Font/BdFont.pm +CPAN/PDF/API2/Resource/Font/CoreFont.pm CPAN/PDF/API2/Resource/Font/CoreFont/bankgothic.pm -CPAN/PDF/API2/Resource/Font/CoreFont/courierboldoblique.pm +CPAN/PDF/API2/Resource/Font/CoreFont/courier.pm CPAN/PDF/API2/Resource/Font/CoreFont/courierbold.pm +CPAN/PDF/API2/Resource/Font/CoreFont/courierboldoblique.pm CPAN/PDF/API2/Resource/Font/CoreFont/courieroblique.pm -CPAN/PDF/API2/Resource/Font/CoreFont/courier.pm -CPAN/PDF/API2/Resource/Font/CoreFont/georgiabolditalic.pm +CPAN/PDF/API2/Resource/Font/CoreFont/georgia.pm CPAN/PDF/API2/Resource/Font/CoreFont/georgiabold.pm +CPAN/PDF/API2/Resource/Font/CoreFont/georgiabolditalic.pm CPAN/PDF/API2/Resource/Font/CoreFont/georgiaitalic.pm -CPAN/PDF/API2/Resource/Font/CoreFont/georgia.pm -CPAN/PDF/API2/Resource/Font/CoreFont/helveticaboldoblique.pm +CPAN/PDF/API2/Resource/Font/CoreFont/helvetica.pm CPAN/PDF/API2/Resource/Font/CoreFont/helveticabold.pm +CPAN/PDF/API2/Resource/Font/CoreFont/helveticaboldoblique.pm CPAN/PDF/API2/Resource/Font/CoreFont/helveticaoblique.pm -CPAN/PDF/API2/Resource/Font/CoreFont/helvetica.pm -CPAN/PDF/API2/Resource/Font/CoreFont.pm CPAN/PDF/API2/Resource/Font/CoreFont/symbol.pm -CPAN/PDF/API2/Resource/Font/CoreFont/timesbolditalic.pm CPAN/PDF/API2/Resource/Font/CoreFont/timesbold.pm +CPAN/PDF/API2/Resource/Font/CoreFont/timesbolditalic.pm CPAN/PDF/API2/Resource/Font/CoreFont/timesitalic.pm CPAN/PDF/API2/Resource/Font/CoreFont/timesroman.pm -CPAN/PDF/API2/Resource/Font/CoreFont/trebuchetbolditalic.pm +CPAN/PDF/API2/Resource/Font/CoreFont/trebuchet.pm CPAN/PDF/API2/Resource/Font/CoreFont/trebuchetbold.pm +CPAN/PDF/API2/Resource/Font/CoreFont/trebuchetbolditalic.pm CPAN/PDF/API2/Resource/Font/CoreFont/trebuchetitalic.pm -CPAN/PDF/API2/Resource/Font/CoreFont/trebuchet.pm -CPAN/PDF/API2/Resource/Font/CoreFont/verdanabolditalic.pm +CPAN/PDF/API2/Resource/Font/CoreFont/verdana.pm CPAN/PDF/API2/Resource/Font/CoreFont/verdanabold.pm +CPAN/PDF/API2/Resource/Font/CoreFont/verdanabolditalic.pm CPAN/PDF/API2/Resource/Font/CoreFont/verdanaitalic.pm -CPAN/PDF/API2/Resource/Font/CoreFont/verdana.pm CPAN/PDF/API2/Resource/Font/CoreFont/webdings.pm CPAN/PDF/API2/Resource/Font/CoreFont/wingdings.pm CPAN/PDF/API2/Resource/Font/CoreFont/zapfdingbats.pm -CPAN/PDF/API2/Resource/Font.pm CPAN/PDF/API2/Resource/Font/Postscript.pm CPAN/PDF/API2/Resource/Font/SynFont.pm CPAN/PDF/API2/Resource/Glyphs.pm CPAN/PDF/API2/Resource/PaperSizes.pm CPAN/PDF/API2/Resource/Pattern.pm -CPAN/PDF/API2/Resource.pm CPAN/PDF/API2/Resource/Shading.pm CPAN/PDF/API2/Resource/UniFont.pm -CPAN/PDF/API2/Resource/uniglyph.txt +CPAN/PDF/API2/Resource/XObject.pm +CPAN/PDF/API2/Resource/XObject/Form.pm +CPAN/PDF/API2/Resource/XObject/Form/BarCode.pm CPAN/PDF/API2/Resource/XObject/Form/BarCode/codabar.pm CPAN/PDF/API2/Resource/XObject/Form/BarCode/code128.pm CPAN/PDF/API2/Resource/XObject/Form/BarCode/code3of9.pm CPAN/PDF/API2/Resource/XObject/Form/BarCode/ean13.pm CPAN/PDF/API2/Resource/XObject/Form/BarCode/int2of5.pm -CPAN/PDF/API2/Resource/XObject/Form/BarCode.pm CPAN/PDF/API2/Resource/XObject/Form/Hybrid.pm -CPAN/PDF/API2/Resource/XObject/Form.pm +CPAN/PDF/API2/Resource/XObject/Image.pm CPAN/PDF/API2/Resource/XObject/Image/GD.pm CPAN/PDF/API2/Resource/XObject/Image/GIF.pm CPAN/PDF/API2/Resource/XObject/Image/JPEG.pm -CPAN/PDF/API2/Resource/XObject/Image.pm CPAN/PDF/API2/Resource/XObject/Image/PNG.pm CPAN/PDF/API2/Resource/XObject/Image/PNM.pm -CPAN/PDF/API2/Resource/XObject/Image/TIFF/File.pm CPAN/PDF/API2/Resource/XObject/Image/TIFF.pm -CPAN/PDF/API2/Resource/XObject.pm +CPAN/PDF/API2/Resource/XObject/Image/TIFF/File.pm +CPAN/PDF/API2/Resource/uniglyph.txt CPAN/PDF/API2/UniWrap.pm CPAN/PDF/API2/Util.pm CPAN/PDF/API2/ViewerPreferences.pm CPAN/PDF/API2/Win32.pm +CPAN/Data/Printer.pm +CPAN/Data/Printer/Common.pm +CPAN/Data/Printer/Config.pm +CPAN/Data/Printer/Filter.pm +CPAN/Data/Printer/Filter/ARRAY.pm +CPAN/Data/Printer/Filter/CODE.pm +CPAN/Data/Printer/Filter/ContentType.pm +CPAN/Data/Printer/Filter/DB.pm +CPAN/Data/Printer/Filter/DateTime.pm +CPAN/Data/Printer/Filter/Digest.pm +CPAN/Data/Printer/Filter/FORMAT.pm +CPAN/Data/Printer/Filter/GLOB.pm +CPAN/Data/Printer/Filter/GenericClass.pm +CPAN/Data/Printer/Filter/HASH.pm +CPAN/Data/Printer/Filter/REF.pm +CPAN/Data/Printer/Filter/Regexp.pm +CPAN/Data/Printer/Filter/SCALAR.pm +CPAN/Data/Printer/Filter/VSTRING.pm +CPAN/Data/Printer/Filter/Web.pm +CPAN/Data/Printer/Object.pm +CPAN/Data/Printer/Profile.pm +CPAN/Data/Printer/Profile/Dumper.pm +CPAN/Data/Printer/Profile/JSON.pm +CPAN/Data/Printer/Theme.pm +CPAN/Data/Printer/Theme/Classic.pm +CPAN/Data/Printer/Theme/Material.pm +CPAN/Data/Printer/Theme/Monokai.pm +CPAN/Data/Printer/Theme/Solarized.pm CPAN/String/Interpolate/Named.pm CPAN/Text/Layout.pm CPAN/Text/Layout/FontConfig.pm CPAN/Text/Layout/FontDescriptor.pm CPAN/Text/Layout/PDFAPI2.pm +CPAN/Text/Layout/PDFAPI2/ImageElement.pm +CPAN/Text/Layout/ElementRole.pm CPAN/Text/Layout/Version.pm diff --git a/Makefile.PL b/Makefile.PL index d0b1dfff..e7667049 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -39,17 +39,18 @@ WriteMakefile # Always require PDF::API2 or PDF::Builder and Text::Layout. PREREQ_PM => { - 'App::Packager' => 1.430, @pdfapi, - 'Text::Layout' => 0.031, + 'Text::Layout' => 0.032, 'JSON::PP' => 2.27203, 'String::Interpolate::Named' => 1.030, - 'File::LoadLines' => 1.021, + 'File::LoadLines' => 1.042, 'File::HomeDir' => 1.004, 'Image::Info' => 1.41, 'List::Util' => 1.46, + 'Data::Printer' => 1.001001, 'Storable' => 3.08, 'Object::Pad' => 0.78, + 'JavaScript::QuickJS' => 0.18, # Some distro's have removed this from the core. 'Pod::Usage' => 2.03, # These are only used by the LaTeX backend diff --git a/README.ABC b/README.ABC index 7fc7acf5..6bb9a6d9 100644 --- a/README.ABC +++ b/README.ABC @@ -12,7 +12,7 @@ there is no ready to use kit, see https://abcplus.sourceforge.net/#abc2svg. ChordPro will try to run a script `abc2svg` with parameters -`toxhtml.js` and the name of a temporary file containing the ABC data. +`tohtml.js` and the name of a temporary file containing the ABC data. The standard output of the script will be scanned for SVG images to insert in the PDF socument. diff --git a/docs/assets/images/ex_svg1.png b/docs/assets/images/ex_svg1.png new file mode 100644 index 00000000..140c54c2 Binary files /dev/null and b/docs/assets/images/ex_svg1.png differ diff --git a/docs/content/ChordPro-Cheat_Sheet.md b/docs/content/ChordPro-Cheat_Sheet.md index aecf75ca..3b83073d 100644 --- a/docs/content/ChordPro-Cheat_Sheet.md +++ b/docs/content/ChordPro-Cheat_Sheet.md @@ -4,7 +4,7 @@ title: "ChordPro Cheat Sheet" description: "ChordPro Cheat Sheet" --- -# ChordPro 6.03 Cheat Sheet +# ChordPro 6.04 Cheat Sheet ## General @@ -43,100 +43,105 @@ Directives can be [conditionally executed](/chordpro-directives/#conditional-dir Arguments to directives may be separated by a colon `:` and/or whitespace. They may be quoted with `""` or `''`. -| Directive | Short | Purpose | Since | -| --- | --- | --- | --- | -| [chord]({{< relref "Directives-chord" >}}) _name_ ... | | Display diagram in-line. | 5.0 | -| ... `base-fret` _base_ | | Specify base-fret (1 or higher).[^1] | 5.0 | -| ... `display` _display_ | | Override chord properties.[^1] | 6.02 | -| ... `fingers` _pos1_ _pos2_ _pos3_ ... | | Specify finger postions.[^1] | 6.0 | -| ... `format` _format_ | | Format string for display purposes.[^1] | 6.02 | -| ... `frets` _pos1_ _pos2_ _pos3_ ... | | Specify fret postions.[^1] | 5.0 | -| ... `keys` _pos1_ _pos2_ _pos3_ ... | | Specify keyboard keys.[^1] | 6.0 | -| [chordcolour]({{< relref "Directives-props_chord_legacy" >}}) _colour_ | | Chord colour. | 5.0 | -| [chordfont]({{< relref "Directives-props_chord_legacy" >}}) _font_ | cf[^3] | Chord font. | 1.0 | -| [chordsize]({{< relref "Directives-props_chord_legacy" >}}) _size_ | cs[^3] | Chord size. | 1.0 | -| [chorus]({{< relref "Directives-env_chorus" >}}) | | Recall chorus. May have a [label]({{< relref "ChordPro-Configuration-PDF#labels" >}}).[^2] | 5.0 | -| [choruscolour]({{< relref "Directives-props_chorus_legacy" >}}) _colour_ | | Chorus colour. | 6.03 | -| [chorusfont]({{< relref "Directives-props_chorus_legacy" >}}) _font_ | | Chorus font. | 6.03 | -| [chorussize]({{< relref "Directives-props_chorus_legacy" >}}) _size_ | | Chorus size. | 6.03 | -| [column_break]({{< relref "Directives-column_break" >}}) | cb | New column or page. | 3.6 | -| [columns]({{< relref "Directives-columns" >}}) _cols_ | col | Number of columns. | 3.6 | -| [comment]({{< relref "Directives-comment" >}}) | c | Comment. | 1.0 | -| [comment_box]({{< relref "Directives-comment" >}}) | cb | Comment. | 3.6 | -| [comment_italic]({{< relref "Directives-comment" >}}) | ci | Comment. | 3.6 | -| [define]({{< relref "Directives-define" >}}) _name_ ... | | Define chord. | 1.0 | -| ... `base-fret` _base_ | | Specify base-fret (1 or higher).[^1] | 1.0 | -| ... `fingers` _pos1_ _pos2_ _pos3_ ... | | Specify finger postions.[^1] | 6.0 | -| ... `frets` _pos1_ _pos2_ _pos3_ ... | | Specify fret postions.[^1] | 1.0 | -| ... `keys` _pos1_ _pos2_ _pos3_ ... | | Specify keyboard keys.[^1] | 6.0 | -| [diagrams]({{< relref "Directives-diagrams" >}}) | | Control diagrams printing | 6.02 | -| [end_of_]({{< relref "Directives-env" >}})_section_ | | Ends a specific section. | 6.0 | -| [end_of_bridge]({{< relref "Directives-env_bridge" >}}) | eob | Ends bridge section. | 6.0 | -| [end_of_chorus]({{< relref "Directives-env_chorus" >}}) | eoc | Ends chorus section. | 1.0 | -| [end_of_grid]({{< relref "Directives-env_grid" >}}) | eog | Ends grid section. | 5.0 | -| [end_of_tab]({{< relref "Directives-env_tab" >}}) | eot | Ends tab section. | 3.6 | -| [end_of_verse]({{< relref "Directives-env_verse" >}}) | eov | Ends verse section. | 6.0 | -| [footersize]({{< relref "Directives-props_footer_legacy" >}}) _size_ | | Footer size. | 5.0 | -| [footercolour]({{< relref "Directives-props_footer_legacy" >}}) _colour_ | | Footer colour. | 5.0 | -| [footerfont]({{< relref "Directives-props_footer_legacy" >}}) _font_ | | Footer font. | 5.0 | -| [grid]({{< relref "Directives-grid_legacy" >}}) | g | Obsolete. See [{diagrams}]({{< relref "Directives-diagrams" >}}). | 3.6 | -| [highlight]({{< relref "Directives-comment" >}}) | | Same as comment. | 5.0 | -| [image]({{< relref "Directives-image" >}}) ... | | Include image. | 5.0 | -| ... `anchor=` _anchor_ | | Anchor for [static image]({{< relref "Directives-image/#static-stationary-images" >}}). | 6.01 | -| ... `border` | | Draws a 1 point border around the image. | 5.0 | -| ... `border=` _width_ | | Draws a border around the image (points). | 5.0 | -| ... `center` | | Center image. | 5.0 | -| ... `center=` _arg_ | | Center image if _arg_ . | 5.0 | -| ... `height=` _height_ | | Height (points). | 5.0 | -| ... `id=` _id_ | | [Asset id](Directives-image#assets). | 6.01 | -| ... `scale=` _scale_ | | Scale factor (number). | 5.0 | -| ... `spread=` _advance_ | | Places the image at the top of the page, across the full page width | 6.0 | -| ... `src=` _filename_ | | Image file name | 5.0 | -| ... `title=` _text_ | | Provides a title (caption) for the image. | 5.0 | -| ... `width=` _width_ | | Width (points). | 5.0 | -| ... `x=` _offset_ | | Horizontal offset (points) for [static image]({{< relref "Directives-image/#static-stationary-images" >}}). | 6.01 | -| ... `y=` _offset_ | | Vertical offset (points) for [static image]({{< relref "Directives-image/#static-stationary-images" >}}). | 6.01 | -| [meta]({{< relref "Directives-meta" >}}) _item_ | | Metadata. | 5.0 | -| [meta album]({{< relref "Directives-album" >}}) _name_ | album | Album name. | 5.0 | -| [meta artist]({{< relref "Directives-artist" >}}) _name_ | artist | Artist name. | 5.0 | -| [meta capo]({{< relref "Directives-capo" >}}) _pos_ | capo | Capo. | 5.0 | -| [meta composer]({{< relref "Directives-composer" >}}) _name_ | composer | Composer name. | 5.0 | -| [meta copyright]({{< relref "Directives-copyright" >}}) _text_ | copyright | Copyright. | 5.0 | -| [meta duration]({{< relref "Directives-duration" >}}) ... | duration | Duration (_mm:ss_ or seconds). | 5.0 | -| [meta key]({{< relref "Directives-key" >}}) _key_ | key | Key. | 5.0 | -| [meta lyricist]({{< relref "Directives-lyricist" >}}) _name_ | lyricist | Lyricist name. | 5.0 | -| [meta sorttitle]({{< relref "Directives-sorttitle" >}}) _text_ | sorttitle | Sort title. | 6.0 | -| [meta tempo]({{< relref "Directives-tempo" >}}) _bpm_ | tempo | Tempo (beats per minute). | 5.0 | -| [meta time]({{< relref "Directives-time" >}}) _n_ `/` _m_ | time | Time signature. | 5.0 | -| [meta year]({{< relref "Directives-year" >}}) _text_ | year | Release date. | 5.0 | -| [new_page]({{< relref "Directives-new_page" >}}) | np | Starts new page. | 3.6 | -| [new_physical_page]({{< relref "Directives-new_page" >}}) | npp | Starts new page. | 3.6 | -| [new_song]({{< relref "Directives-new_song" >}}) | ns | Starts a new song. | 1.0 | -| [no_grid]({{< relref "Directives-grid_legacy" >}}) | ng | Obsolete. See [{diagrams:off}]({{< relref "Directives-diagrams" >}}). | 3.6 | -| [pagetype]({{< relref "Directives-pagetype_legacy" >}}) ...[^4] | | Set page (paper) size. | 4.0 | -| [start_of_]({{< relref "Directives-env" >}})_section_ | | Starts a specific section. May have a [label]({{< relref "ChordPro-Configuration-PDF#labels" >}}). | 6.0 | -| [start_of_bridge]({{< relref "Directives-env_bridge" >}}) | sob | Starts bridge section. May have a [label]({{< relref "ChordPro-Configuration-PDF#labels" >}}). | 6.0 | -| [start_of_chorus]({{< relref "Directives-env_chorus" >}}) | soc | Starts chorus section. May have a [label]({{< relref "ChordPro-Configuration-PDF#labels" >}}).[^2] | 1.0 | -| [start_of_grid]({{< relref "Directives-env_grid" >}}) | sog | Starts grid section. May have a [label]({{< relref "ChordPro-Configuration-PDF#labels" >}}).[^2] | 5.0 | -| [start_of_tab]({{< relref "Directives-env_tab" >}}) | sot | Starts tab section May have a [label]({{< relref "ChordPro-Configuration-PDF#labels" >}}).[^2] | 3.6 | -| [start_of_verse]({{< relref "Directives-env_verse" >}}) | sov | Starts verse section. May have a [label]({{< relref "ChordPro-Configuration-PDF#labels" >}}). | 6.0 | -| [subtitle]({{< relref "Directives-subtitle" >}}) _text_ | st | Subtitle for song. | 1.0 | -| [tabcolour]({{< relref "Directives-props_tab_legacy" >}}) _colour_ | | Tabs colour. | 5.0 | -| [tabfont]({{< relref "Directives-props_tab_legacy" >}}) _font_ | | Tabs font. | 5.0 | -| [tabsize]({{< relref "Directives-props_tab_legacy" >}}) _size_ | | Tabs size. | 5.0 | -| [textcolour]({{< relref "Directives-props_text_legacy" >}}) _colour_ | | Text colour. | 5.0 | -| [textfont]({{< relref "Directives-props_text_legacy" >}}) _font_ | tf[^3] | Text font. | 1.0 | -| [textsize]({{< relref "Directives-props_text_legacy" >}}) _size_ | ts[^3] | Text size. | 1.0 | -| [title]({{< relref "Directives-title" >}}) _text_ | t | Title for song. | 1.0 | -| [titlesize]({{< relref "Directives-props_title_legacy" >}}) _size_ | | Title size. | 5.0 | -| [titlecolour]({{< relref "Directives-props_title_legacy" >}}) _colour_ | | Title colour. | 5.0 | -| [titlefont]({{< relref "Directives-props_title_legacy" >}}) _font_ | | Title font. | 5.0 | -| [titles]({{< relref "Directives-titles_legacy" >}}) _flush_ | | Flush titles (`center`, `left` or `right`). | 3.6.4 | -| [tocsize]({{< relref "Directives-props_toc_legacy" >}}) _size_ | | Table of contents font size. | 5.0 | -| [toccolour]({{< relref "Directives-props_toc_legacy" >}}) _colour_ | | Table of contents colour. | 5.0 | -| [tocfont]({{< relref "Directives-props_toc_legacy" >}}) _font_ | | Table of contents font. | 5.0 | -| [transpose]({{< relref "Directives-transpose" >}}) _semitones_ | | Transpose by a number of semitones. | 5.0 | -| [x_...]({{< relref "Directives-custom" >}}) | | Custom directive. | 5.0 | +| Directive | Short | Purpose | Since | +|--------------------------------------------------------------------------|-----------|-------------------------------------------------------------------------------------------------------------|-------| +| [chord]({{< relref "Directives-chord" >}}) _name_ ... | | Display diagram in-line. | 5.0 | +| ... `base-fret` _base_ | | Specify base-fret (1 or higher).[^1] | 5.0 | +| ... `diagram` _display_ | | Override diagram display. May be a colour. | 6.03 | +| ... `display` _display_ | | Override chord properties.[^1] | 6.02 | +| ... `fingers` _pos1_ _pos2_ _pos3_ ... | | Specify finger postions.[^1] | 6.0 | +| ... `format` _format_ | | Format string for display purposes.[^1] | 6.02 | +| ... `frets` _pos1_ _pos2_ _pos3_ ... | | Specify fret postions.[^1] | 5.0 | +| ... `keys` _pos1_ _pos2_ _pos3_ ... | | Specify keyboard keys.[^1] | 6.0 | +| [chordcolour]({{< relref "Directives-props_chord_legacy" >}}) _colour_ | | Chord colour. | 5.0 | +| [chordfont]({{< relref "Directives-props_chord_legacy" >}}) _font_ | cf[^3] | Chord font. | 1.0 | +| [chordsize]({{< relref "Directives-props_chord_legacy" >}}) _size_ | cs[^3] | Chord size. | 1.0 | +| [chorus]({{< relref "Directives-env_chorus" >}}) | | Recall chorus. May have a [label]({{< relref "ChordPro-Configuration-PDF#labels" >}}).[^2] | 5.0 | +| [choruscolour]({{< relref "Directives-props_chorus_legacy" >}}) _colour_ | | Chorus colour. | 6.03 | +| [chorusfont]({{< relref "Directives-props_chorus_legacy" >}}) _font_ | | Chorus font. | 6.03 | +| [chorussize]({{< relref "Directives-props_chorus_legacy" >}}) _size_ | | Chorus size. | 6.03 | +| [column_break]({{< relref "Directives-column_break" >}}) | cb | New column or page. | 3.6 | +| [columns]({{< relref "Directives-columns" >}}) _cols_ | col | Number of columns. | 3.6 | +| [comment]({{< relref "Directives-comment" >}}) | c | Comment. | 1.0 | +| [comment_box]({{< relref "Directives-comment" >}}) | cb | Comment. | 3.6 | +| [comment_italic]({{< relref "Directives-comment" >}}) | ci | Comment. | 3.6 | +| [define]({{< relref "Directives-define" >}}) _name_ ... | | Define chord. | 1.0 | +| ... `base-fret` _base_ | | Specify base-fret (1 or higher).[^1] | 1.0 | +| ... `diagram` _display_ | | Override diagram display. May be a colour. | 6.03 | +| ... `display` _display_ | | Override chord properties.[^1] | 6.02 | +| ... `fingers` _pos1_ _pos2_ _pos3_ ... | | Specify finger postions.[^1] | 6.0 | +| ... `format` _format_ | | Format string for display purposes.[^1] | 6.02 | +| ... `frets` _pos1_ _pos2_ _pos3_ ... | | Specify fret postions.[^1] | 1.0 | +| ... `keys` _pos1_ _pos2_ _pos3_ ... | | Specify keyboard keys.[^1] | 6.0 | +| [diagrams]({{< relref "Directives-diagrams" >}}) | | Control diagrams printing | 6.02 | +| [end_of_]({{< relref "Directives-env" >}})_section_ | | Ends a specific section. | 6.0 | +| [end_of_bridge]({{< relref "Directives-env_bridge" >}}) | eob | Ends bridge section. | 6.0 | +| [end_of_chorus]({{< relref "Directives-env_chorus" >}}) | eoc | Ends chorus section. | 1.0 | +| [end_of_grid]({{< relref "Directives-env_grid" >}}) | eog | Ends grid section. | 5.0 | +| [end_of_tab]({{< relref "Directives-env_tab" >}}) | eot | Ends tab section. | 3.6 | +| [end_of_verse]({{< relref "Directives-env_verse" >}}) | eov | Ends verse section. | 6.0 | +| [footersize]({{< relref "Directives-props_footer_legacy" >}}) _size_ | | Footer size. | 5.0 | +| [footercolour]({{< relref "Directives-props_footer_legacy" >}}) _colour_ | | Footer colour. | 5.0 | +| [footerfont]({{< relref "Directives-props_footer_legacy" >}}) _font_ | | Footer font. | 5.0 | +| [grid]({{< relref "Directives-grid_legacy" >}}) | g | Obsolete. See [{diagrams}]({{< relref "Directives-diagrams" >}}). | 3.6 | +| [highlight]({{< relref "Directives-comment" >}}) | | Same as comment. | 5.0 | +| [image]({{< relref "Directives-image" >}}) ... | | Include image. | 5.0 | +| ... `anchor=` _anchor_ | | Anchor for [static image]({{< relref "Directives-image/#static-stationary-images" >}}). | 6.01 | +| ... `border` | | Draws a 1 point border around the image. | 5.0 | +| ... `border=` _width_ | | Draws a border around the image (points). | 5.0 | +| ... `center` | | Center image. | 5.0 | +| ... `center=` _arg_ | | Center image if _arg_ . | 5.0 | +| ... `height=` _height_ | | Height (points). | 5.0 | +| ... `id=` _id_ | | [Asset id](Directives-image#assets). | 6.01 | +| ... `scale=` _scale_ | | Scale factor (number). | 5.0 | +| ... `spread=` _advance_ | | Places the image at the top of the page, across the full page width | 6.0 | +| ... `src=` _filename_ | | Image file name | 5.0 | +| ... `title=` _text_ | | Provides a title (caption) for the image. | 5.0 | +| ... `width=` _width_ | | Width (points). | 5.0 | +| ... `x=` _offset_ | | Horizontal offset (points) for [static image]({{< relref "Directives-image/#static-stationary-images" >}}). | 6.01 | +| ... `y=` _offset_ | | Vertical offset (points) for [static image]({{< relref "Directives-image/#static-stationary-images" >}}). | 6.01 | +| [meta]({{< relref "Directives-meta" >}}) _item_ | | Metadata. | 5.0 | +| [meta album]({{< relref "Directives-album" >}}) _name_ | album | Album name. | 5.0 | +| [meta artist]({{< relref "Directives-artist" >}}) _name_ | artist | Artist name. | 5.0 | +| [meta capo]({{< relref "Directives-capo" >}}) _pos_ | capo | Capo. | 5.0 | +| [meta composer]({{< relref "Directives-composer" >}}) _name_ | composer | Composer name. | 5.0 | +| [meta copyright]({{< relref "Directives-copyright" >}}) _text_ | copyright | Copyright. | 5.0 | +| [meta duration]({{< relref "Directives-duration" >}}) ... | duration | Duration (_mm:ss_ or seconds). | 5.0 | +| [meta key]({{< relref "Directives-key" >}}) _key_ | key | Key. | 5.0 | +| [meta lyricist]({{< relref "Directives-lyricist" >}}) _name_ | lyricist | Lyricist name. | 5.0 | +| [meta sorttitle]({{< relref "Directives-sorttitle" >}}) _text_ | sorttitle | Sort title. | 6.0 | +| [meta tempo]({{< relref "Directives-tempo" >}}) _bpm_ | tempo | Tempo (beats per minute). | 5.0 | +| [meta time]({{< relref "Directives-time" >}}) _n_ `/` _m_ | time | Time signature. | 5.0 | +| [meta year]({{< relref "Directives-year" >}}) _text_ | year | Release date. | 5.0 | +| [new_page]({{< relref "Directives-new_page" >}}) | np | Starts new page. | 3.6 | +| [new_physical_page]({{< relref "Directives-new_page" >}}) | npp | Starts new page. | 3.6 | +| [new_song]({{< relref "Directives-new_song" >}}) ... | ns | Starts a new song. | 1.0 | +| ... `toc=` _arg_ | | Enter the title in the table of contents if _arg_ (default) | 6.04 | +| [no_grid]({{< relref "Directives-grid_legacy" >}}) | ng | Obsolete. See [{diagrams:off}]({{< relref "Directives-diagrams" >}}). | 3.6 | +| [pagetype]({{< relref "Directives-pagetype_legacy" >}}) ...[^4] | | Set page (paper) size. | 4.0 | +| [start_of_]({{< relref "Directives-env" >}})_section_ | | Starts a specific section. May have a [label]({{< relref "ChordPro-Configuration-PDF#labels" >}}). | 6.0 | +| [start_of_bridge]({{< relref "Directives-env_bridge" >}}) | sob | Starts bridge section. May have a [label]({{< relref "ChordPro-Configuration-PDF#labels" >}}). | 6.0 | +| [start_of_chorus]({{< relref "Directives-env_chorus" >}}) | soc | Starts chorus section. May have a [label]({{< relref "ChordPro-Configuration-PDF#labels" >}}).[^2] | 1.0 | +| [start_of_grid]({{< relref "Directives-env_grid" >}}) | sog | Starts grid section. May have a [label]({{< relref "ChordPro-Configuration-PDF#labels" >}}).[^2] | 5.0 | +| [start_of_tab]({{< relref "Directives-env_tab" >}}) | sot | Starts tab section May have a [label]({{< relref "ChordPro-Configuration-PDF#labels" >}}).[^2] | 3.6 | +| [start_of_verse]({{< relref "Directives-env_verse" >}}) | sov | Starts verse section. May have a [label]({{< relref "ChordPro-Configuration-PDF#labels" >}}). | 6.0 | +| [subtitle]({{< relref "Directives-subtitle" >}}) _text_ | st | Subtitle for song. | 1.0 | +| [tabcolour]({{< relref "Directives-props_tab_legacy" >}}) _colour_ | | Tabs colour. | 5.0 | +| [tabfont]({{< relref "Directives-props_tab_legacy" >}}) _font_ | | Tabs font. | 5.0 | +| [tabsize]({{< relref "Directives-props_tab_legacy" >}}) _size_ | | Tabs size. | 5.0 | +| [textcolour]({{< relref "Directives-props_text_legacy" >}}) _colour_ | | Text colour. | 5.0 | +| [textfont]({{< relref "Directives-props_text_legacy" >}}) _font_ | tf[^3] | Text font. | 1.0 | +| [textsize]({{< relref "Directives-props_text_legacy" >}}) _size_ | ts[^3] | Text size. | 1.0 | +| [title]({{< relref "Directives-title" >}}) _text_ | t | Title for song. | 1.0 | +| [titlesize]({{< relref "Directives-props_title_legacy" >}}) _size_ | | Title size. | 5.0 | +| [titlecolour]({{< relref "Directives-props_title_legacy" >}}) _colour_ | | Title colour. | 5.0 | +| [titlefont]({{< relref "Directives-props_title_legacy" >}}) _font_ | | Title font. | 5.0 | +| [titles]({{< relref "Directives-titles_legacy" >}}) _flush_ | | Flush titles (`center`, `left` or `right`). | 3.6.4 | +| [tocsize]({{< relref "Directives-props_toc_legacy" >}}) _size_ | | Table of contents font size. | 5.0 | +| [toccolour]({{< relref "Directives-props_toc_legacy" >}}) _colour_ | | Table of contents colour. | 5.0 | +| [tocfont]({{< relref "Directives-props_toc_legacy" >}}) _font_ | | Table of contents font. | 5.0 | +| [transpose]({{< relref "Directives-transpose" >}}) _semitones_ | | Transpose by a number of semitones. | 5.0 | +| [x_...]({{< relref "Directives-custom" >}}) | | Custom directive. | 5.0 | { .table .table-striped .table-bordered .table-sm } ### Notes @@ -148,6 +153,12 @@ feature. There has never been an official [5.0 release]({{< relref "ChordPro5-RelNotes.md" >}}). 5.0 was mostly an intermediate development target for 6.0. +As of version 6, the version numbering is according to [semantic +versioning](https://semver.org/). However, to obtain a defined sorting +order of file names a two-digit minor version is combined with a +single digit patch number to form a single three-digit fraction. +`6.030` is version `6`, minor `3`, patch `0`. + [^1]: As of [version 6.0]({{< relref "ChordPro6-RelNotes.md" >}}), all arguments to `chord` and `define` directives are optional. [^2]: [Section labels]({{< relref "ChordPro-Configuration-PDF#labels" >}}) are a 6.0 feature. diff --git a/docs/content/ChordPro-Chords.md b/docs/content/ChordPro-Chords.md index e7af9fe6..9db222f2 100644 --- a/docs/content/ChordPro-Chords.md +++ b/docs/content/ChordPro-Chords.md @@ -128,6 +128,8 @@ notes before the chords: # Appendix: List of known chord extensions +Note that _extensions_ here include the _qualifier_. + The following chord extensions are currently built-in. ### Extensions for major chords diff --git a/docs/content/ChordPro-Configuration-Delegates.md b/docs/content/ChordPro-Configuration-Delegates.md new file mode 100644 index 00000000..fc433390 --- /dev/null +++ b/docs/content/ChordPro-Configuration-Delegates.md @@ -0,0 +1,180 @@ +--- +title: "Configuration file contents - Delegates" +description: "Configuration file contents - Delegates" +--- + +# Configuration file contents - Delegates + +{{< toc >}} + +Delegates are interfaces to external tools to turn non-ChordPro +content into something that ChordPro can handle. The non-ChordPro +content is included in the song between `{start_of_...}` and +`{end_of_...}` directives. + +ChordPro currently provides two delegates: + +* abc, the popular [ABC music notation](https://www.abcnotation.com), and + +* ly, the [Lilypond](https://lilypond.org) music typesetter. + +Configuration settings for these delegates can be set the config file +under `"delegates"`: + + "delegates" : { + "abc" : { ... }, + "ly" : { ... }, + }, + +## ABC + +The ABC delegate collects the ABC music data and prepares it for the +ABC tool to turn it into an SVG image. + +### Selecting the ABC tool + +The ABC delegate supports two tools: + +* `abc2svg`, a program that comes with some ABC tools. + +* QuickJS, a JavaScript interpreter. + +ChordPro will first try to find an `abc2svg` program in the executable +path. If this is found it will be used to process the ABC data. The +program will be executed with a single argument, the name of a file +that contains the prepared ABC data, and it should produce the SVG +image on standard output. + +Alternatively, if the QuickJS program `qjs` can be found in the +executable path, ChordPro will use this internally to produce the SVG +image. + +In the ABC configuration the setting `handler` can be use to modify +the tool selection. If set to `"abc2svg"` selection proceeds as +described above. If set to `"abc2svg_qjs"` ChordPro will always use the +QuickJS interpreter. + +### ABC configuration + + "abc" : { + "module" : "ABC", + "handler" : "abc2svg", + "type" : "image", + + // The preamble is a list of lines inserted before the ABC data. + // DO NOT MODIFY unless you know what you are doing! + "preamble" : [ + // Get rid of as much space as possible. + "%%topspace 0", + "%%titlespace 0", + "%%musicspace 0", + "%%composerspace 0", + "%%infospace 0", + "%%textspace 0", + "%%leftmargin 0cm", + "%%rightmargin 0cm", + "%%staffsep 0", + // Use ChordPro fonts for lyrics and chords. + "%%textfont pdf.fonts.text", + "%%gchordfont pdf.fonts.chord", + ], + + "preprocess" : { "abc" : [] }, + "omit" : false, + }, + +* `module`: The (perl) module that implements this delegate. + This must be `"ABC"`. + +* `handler`: The module function that handles this delegation. + Valid values are `"abc2svg"` and `"abc2svg_qjs"`. + +* `type`: The result produced by the delegate handler. + This must be `"image"`, this delegate + produces an image that will be embedded in the ChordPro output. + +* `preamble`: A series of ABC directives that are prepended to the ABC + data to make sure that the generated image can be nicely embedded in + the ChordPro output. + +* `preprocess`: A preprocessor of the ABC data. + See [Parser]({{< relref "ChordPro-Configuration-Parser" >}}) for + a description of preprocessors. + +* `omit`: If `true`, no delegation will be handled. In other words, + the contents of `{start_of_abc}` ... `{end_of_abc}` is silently + ignored. + +### ABC using ChordPro fonts + +ABC directives can refer to ChordPro fonts with a `pdf.fonts.` prefix. +See the preamble in the ABC config above for some examples. + +### ABC using arbitrary fonts + +When arbitrary fonts are used in the ABC data it is important that +ChordPro knows these fonts as well. For each of these fonts an entry +must be made in the font registry. + +For example, in the ABC the music font `Bravura` is used. The +corresponding ChordPro font registry would be: + + "pdf" : { + "fontconfig" : { + ... + "bravura" : { + "" : { "file" : "Bravura.otf" } + }, + ... + } + } + +See also [ChordPro Implementation: Fonts]({{< +relref "chordpro-fonts" >}}). + +## Lilypond + +The Lilypond delegate collects the Lilypond music data and prepares it +for the Lilypond program `lilypond` to turn it into an SVG image. + +### Lilypond configuration + + "ly" : { + "module" : "Lilypond", + "handler" : "ly2svg", + "type" : "image", + + // The preamble is a list of lines inserted before the lilipond data. + // This is a good place to set the version and global customizations. + "preamble" : [ + "\\version \"2.21.0\"", + "\\header { tagline = ##f }", + ], + + "omit" : false, + }, + +* `module`: The (perl) module that implements this delegate. + This must be `"Lilypond"`. + +* `handler`: The module function that handles this delegation. + This must be `"ly2svg"`. + +* `type`: The result produced by the delegate handler. + This must be `"image"`, this delegate + produces an image that will be embedded in the ChordPro output. + +* `preamble`: A series of Lilypond directives that are prepended to + the Lilypond data to make sure that the generated image can be + nicely embedded in the ChordPro output. + Note that Lilypond directives start with a backslash, which has a + special meaning in JSON data. Two consecutive backslashes will be + interpretated as a single backslash without special meaning. + +* `omit`: If `true`, no delegation will be handled. In other words, + the content of `{start_of_ly}` ... `{end_of_ly}` is silently + ignored. + +### Connecting Lilypond and ChordPro fonts + +This is similar to [ABC using arbitrary fonts](#abc-using-arbitrary-fonts). diff --git a/docs/content/ChordPro-Configuration-File.md b/docs/content/ChordPro-Configuration-File.md index b1e04399..b65ea119 100644 --- a/docs/content/ChordPro-Configuration-File.md +++ b/docs/content/ChordPro-Configuration-File.md @@ -31,7 +31,7 @@ Layout doesn't matter, this document might as well have been written as: {"diagrams":{"show":"none"}} -The ChordPro configuration file consists of two parts, all optional. +The ChordPro configuration file consists of the following parts, all optional. * [The Generic Part]({{< relref "ChordPro-Configuration-Generic" >}}) Settings for the song parser and output backends. @@ -45,6 +45,9 @@ Specific settings for HTML output. * [ChordPro Output]({{< relref "ChordPro-Configuration-ChordPro" >}}) Specific settings for ChordPro output. +* [Delegate Configuration]({{< relref "ChordPro-Configuration-Delegates" >}}) +Settings for _delegates_, e.g. ABC and Lilypond. + * [ASCII text to ChordPro converter]({{< relref "ChordPro-Configuration-A2Crd" >}}) Settings for the ASCII text to ChordPro converter. diff --git a/docs/content/ChordPro-Configuration-Overview.md b/docs/content/ChordPro-Configuration-Overview.md index 08fcd75a..81b1e9d2 100644 --- a/docs/content/ChordPro-Configuration-Overview.md +++ b/docs/content/ChordPro-Configuration-Overview.md @@ -98,31 +98,6 @@ In the examples below the symbol `~` denotes the user's home directory. Windows **Important** A song specific configuration file may **not** contain an `"include"` or `"tuning"` item. -## Configuration resources (presets) - -Preset configurations can be specified by their names, without path -and extension. For example, `--config=modern1` refers to the preset -`modern1`. - -When a preset config is used, ChordPro will try to find the -corresponding config file in the following locations (using `modern1` -as example): - -* File `config/modern1.json` or `config/modern1.prp` in one of the - paths from the `CHORDPRO_LIB` search path, provided this - environment variable has been set. - -* File `config/modern1.json` in a built-in location that comes with - the ChordPro install. - -If environment variable `CHORDPRO_LIB` is not explicitly set, -ChordPro will use the first valid directory of: - -`$XDG_CONFIG_HOME/chordpro` (provided variable `XDG_CONFIG_HOME` is set) -`$HOME/.config/chordpro` -`$HOME/.chordpro` -`$HOME/chordpro` - ## How config files are combined The config files are processed in order, and their contents are merged. In general, a config setting from a later file replaces the value from previous files. There are a few exceptions: instrument definitions, hashes and arrays. diff --git a/docs/content/ChordPro-Configuration-PDF.md b/docs/content/ChordPro-Configuration-PDF.md index 384e9c58..93bbe448 100644 --- a/docs/content/ChordPro-Configuration-PDF.md +++ b/docs/content/ChordPro-Configuration-PDF.md @@ -159,6 +159,10 @@ but only if labels are actually used. `align` will control how the labels are aligned in the margin. +The appearance of the label text can be set in the [fonts]( {{< relref +"#fonts" >}}). +It defaults to the text (lyrics) font and size. + {{< showpage "page_labels" >}} ## Chorus style diff --git a/docs/content/ChordPro-Directives.md b/docs/content/ChordPro-Directives.md index cb277c20..7dba42cc 100644 --- a/docs/content/ChordPro-Directives.md +++ b/docs/content/ChordPro-Directives.md @@ -81,6 +81,7 @@ output. * [start_of_abc]({{< relref "Directives-env_abc" >}}) / [end_of_abc]({{< relref "Directives-env_abc" >}}) * [start_of_ly]({{< relref "Directives-env_ly" >}}) / [end_of_ly]({{< relref "Directives-env_ly" >}}) +* [start_of_svg]({{< relref "Directives-env_svg" >}}) / [end_of_ly]({{< relref "Directives-env_svg" >}}) ## Chord diagrams diff --git a/docs/content/ChordPro-Fonts.md b/docs/content/ChordPro-Fonts.md index 0f71eb2d..5ba6e2c0 100644 --- a/docs/content/ChordPro-Fonts.md +++ b/docs/content/ChordPro-Fonts.md @@ -101,7 +101,7 @@ In the config file in section `"pdf"` there is a section `"fontconfig"` that can }, }, -For each family name you should specify four members: a regular font (with an empty key), a bold font (key `"bold"` or `"b"`), an italic font (key `"italic"` or `"i"` or `"oblique"` or `"o"`), and a bold-italic font. +For each family name you should specify four members: a regular font (with an empty key), a bold font (key `"bold"`), an italic font (key `"italic"`), and a bold-italic font. This is the short story. The longer story is that instead of a file name you can specify another set of key/value pairs, for example: @@ -171,23 +171,19 @@ and associating output elements to font families. The latter part is // "fontconfig" maps members of font families to built-in fonts. "fontconfig" : { - // alternatives: regular r normal - // alternatives: bold b strong - // alternatives: italic i oblique o emphasis - // alternatives: bolditalic bi italicbold ib boldoblique bo obliquebold ob "serif" : { "" : "Times-Roman", "bold" : "Times-Bold", "italic" : "Times-Italic", "bolditalic" : "Times-BoldItalic", }, - "sans" : { + "sans, sans-serif" : { "" : "Helvetica", "bold" : "Helvetica-Bold", - "oblique" : "Helvetica-Oblique", - "boldoblique" : "Helvetica-BoldOblique", + "italic" : "Helvetica-Oblique", + "bolditalic" : "Helvetica-BoldOblique", }, - "monospace" : { + "mono, monospace" : { "" : "Courier", "bold" : "Courier-Bold", "italic" : "Courier-Italic", @@ -257,23 +253,19 @@ and associating output elements to font families. The latter part is // "fontconfig" maps members of font families to font files. "fontconfig" : { - // alternatives: regular r normal - // alternatives: bold b strong - // alternatives: italic i oblique o emphasis - // alternatives: bolditalic bi italicbold ib boldoblique bo obliquebold ob "serif" : { "" : "LiberationSerif-Regular.ttf", "bold" : "LiberationSerif-Bold.ttf", "italic" : "LiberationSerif-Italic.ttf", "bolditalic" : "LiberationSerif-BoldItalic.ttf", }, - "sans" : { + "sans, sans-serif" : { "" : "LiberationSans-Regular.ttf", "bold" : "LiberationSans-Bold.ttf", - "oblique" : "LiberationSans-Italic.ttf", - "boldoblique" : "LiberationSans-BoldItalic.ttf", + "italic" : "LiberationSans-Italic.ttf", + "bolditalic" : "LiberationSans-BoldItalic.ttf", }, - "monospace" : { + "mono, monospace" : { "" : "LiberationMono-Regular.ttf", "bold" : "LiberationMono-Bold.ttf", "italic" : "LiberationMono-Italic.ttf", @@ -339,23 +331,19 @@ and associating output elements to font families. The latter part is // "fontconfig" maps members of font families to physical fonts. "fontconfig" : { - // alternatives: regular r normal - // alternatives: bold b strong - // alternatives: italic i oblique o emphasis - // alternatives: bolditalic bi italicbold ib boldoblique bo obliquebold ob "serif" : { "" : "georgia.ttf", "bold" : "georgiab.ttf", "italic" : "georgiai.ttf", "bolditalic" : "georgiaz.ttf", }, - "sans" : { + "sans, sans-serif" : { "" : "arial.ttf", "bold" : "arialbd.ttf", - "oblique" : "ariali.ttf", - "boldoblique" : "arialbi.ttf", + "italic" : "ariali.ttf", + "bolditalic" : "arialbi.ttf", }, - "monospace" : { + "mono, monospace" : { "" : "cour.ttf", "bold" : "courbd.ttf", "italic" : "couri.ttf", diff --git a/docs/content/ChordPro-History.md b/docs/content/ChordPro-History.md index 4376d6a6..2c9e6ed6 100644 --- a/docs/content/ChordPro-History.md +++ b/docs/content/ChordPro-History.md @@ -112,6 +112,10 @@ The first release of `ChordPro`, an alpha version, was on June 4, 2016. | 5.980 | 2021-08-13 | development for ChordPro 6 | | 10 releases later | | | | 5.990 | 2022-11-03 | pre-release for ChordPro 6 | +| 6.000 | 2022-12-28 | ChordPro 6 | +| 6.010 | 2023-06-05 | ChordPro 6.01 | +| 6.020 | 2023-07-21 | ChordPro 6.02 | +| 6.030 | 2023-09-18 | ChordPro 6.03 | { .table .table-striped .table-bordered .table-sm } Note that ChordPro 5 was never officially released, but went straight diff --git a/docs/content/ChordPro-Reference-RelNotes.md b/docs/content/ChordPro-Reference-RelNotes.md index 896b6d71..844f1f3f 100644 --- a/docs/content/ChordPro-Reference-RelNotes.md +++ b/docs/content/ChordPro-Reference-RelNotes.md @@ -1,5 +1,62 @@ # Release info +## 6.040 + +Released: 2023-12-26 + + +### Highlights + +* Images can be [placed everywhere](https://chordpro.org/chordpro/directives-image/). They can be placed relative to the paper, the page, the column, and the lyrics. +* Images can be [embedded[(https://chordpro.org/chordpro/directives-image/#inline-images) in text (lyrics) lines, either as part of the text similar to a glyph, or somewhere else on the page relative to a particular place in the text. The latter is most interesting for annotations. +* Delegates are images too. Annotate your lyrics with SVG images, or with musical notes using ABC or Lilypond. +* Chord and keyboard diagrams are images too. And you can use string and keyboard diagrams simultaneously. +* Resources like configs, tasks and images are now more logically searched using [resource libraries](https://chordpro.org/chordpro/resources/). + +### ChordPro functionality + +* Rework paths handling for consistent resource handling; eliminate App::Packager. +* Inline images. +* {image} can have label and align properties. +* {define}d chords overrules suppress list. +* (ABC) Use QuickJS XS (JavaScript::QuickJS) as preferred. +* (ABC) ABC embedding no longer uses nodeJS (npx). +* (ABC) Make split work (and enable by default). +* (ABC) Images are left aligned by default. +* (Lilypond) Images are left aligned by default. +* Improve runtime info. +* Suppress songs that do not have content. +* Suppress table of content entry for a song w/ {ns toc=no}. +* (PDF) Image scale strategy change for spread images. +* (PDF/Writer) Add generalized add_object for objects and images. +* (PDF) Prevent case problems when looking up fonts for SVG. +* (PDF) Add aliases for web standard fonts like serif, sans, ... +* (PDF) Ignore leading empty and ignores (was: leading empty only). +* (Windows) ChordPro now installs as a 64-bit application in \Program Files instead of \Program Files (x86). You are adsvised to remove the old 32bits install first. + +### Bug fixes + +* Prevent warning when parsing {key} and trancode to nashville/roman. +* Fix chord inversion (issue 321). +* Fix comment lines disturbing a consecutive series of {chord}s. +* Fix typo in Wx Preferencesdialog, causing it to crash. +* Fix problem with PDF/SVG caching fonts. +* Fix comment labels for delegates (issue 329.3). +* (Wx) Filter configs on prp ans json. +* Fix memorize/recall/transpose issue 333. +* Fix issue 334. + +### Internal + +* (ABC) ABC embedding use tohtml instead of toxhtml. +* (PDF) Enhance assets (wip), labels; move grid to separate module. +* Experimental ##include facility. + +### Social and support + +[User community](https://groups.io/g/ChordPro) for feedback and help. +Please use the [issue tracker](https://github.com/ChordPro/chordpro/issues) for bugs reports. + ## 6.030 Released: 2023-09-18 @@ -39,11 +96,6 @@ Released: 2023-09-18 * Fix issue #309. * Fix issue #311. -### Social and support - -[User community](https://groups.io/g/ChordPro) for feedback and help. -Please use the [issue tracker](https://github.com/ChordPro/chordpro/issues) for bugs reports. - ## 6.020 Released: 2023-07-21 diff --git a/docs/content/Directives-env.md b/docs/content/Directives-env.md index b3bf18cd..6a40547a 100644 --- a/docs/content/Directives-env.md +++ b/docs/content/Directives-env.md @@ -14,25 +14,34 @@ Environments start with a `start_of` directive, e.g. e.g. `{end_of_chorus}`. As with every ChordPro directive, these directives should be alone on a line. -You are free to choose names for sections as long as the names only -consists of letters, digits and underscores. All environments are -considered to be part of the song lyrics, except for `chorus`, `tab`, -and `grid`. These environments get a predefined special treatment. +You can choose arbitrary names for sections as long as the names only +consists of letters, digits and underscores. Environments `chorus`, +`tab`, and `grid` get a predefined special treatment. -All environment directives may include an optional label to identify -the section. For example:, +Implementations are free to add special treatment to specific +environments, but unknown (unhandled) environments should always be +treated as part of the song lyrics. + +All environment directives may include an optional [label]({{< relref +"ChordPro-Configuration-PDF#labels" >}}) to identify the section. For +example:, + + {start_of_verse: label="Verse 1"} + +For backward compatibility, this also works: {start_of_verse: Verse 1} -The ChordPro reference implementation prints the label in the left -margin, see [labels]({{< relref "ChordPro-Configuration-PDF#labels" >}}). +The label text may contain `\n` sequences to produce multi--line +labels: + + {start_of_verse: label="Verse 1\nAll"} For legacy reasons, the following environments have a short directive to start and end them: * [start_of_chorus]({{< relref "Directives-env_chorus" >}}) (short: soc) * [end_of_chorus]({{< relref "Directives-env_chorus" >}}) (short: eoc) -* [chorus]({{< relref "Directives-env_chorus" >}}) * [start_of_verse]({{< relref "Directives-env_verse" >}}) (short: sov) * [end_of_verse]({{< relref "Directives-env_verse" >}}) (short: eov) * [start_of_bridge]({{< relref "Directives-env_bridge" >}}) (short: sob) diff --git a/docs/content/Directives-env_abc.md b/docs/content/Directives-env_abc.md index 7b4b0d14..41c26a81 100644 --- a/docs/content/Directives-env_abc.md +++ b/docs/content/Directives-env_abc.md @@ -31,6 +31,10 @@ left margin. For example:, {start_of_abc: Intro} +or (preferred): + + {start_of_abc: label="Intro"} + The ChordPro reference implementation prints the label in the left margin, see [labels]({{< relref "ChordPro-Configuration-PDF#labels" >}}). @@ -42,6 +46,9 @@ margin, see [labels]({{< relref "ChordPro-Configuration-PDF#labels" >}}). line will be considered formatting instructions (see below). See also [Remarks]({{< relref "#remarks" >}}). +* The song **must** have a key (`K:`). + **No output will be generated if there is no key.** + * ChordPro transposition using `{transpose}` or `--transpose` will transpose the embedded ABC. Adding `%%transpose` to the ABC source will affect the ABC notes only. @@ -64,7 +71,29 @@ margin, see [labels]({{< relref "ChordPro-Configuration-PDF#labels" >}}). Since the actual rendering is handled by external tools, ChordPro has no control over what and how the output will look like. -## Formatting instructions +## Attributes + +The ABC directive may contain the same formatting attributes as the +image directive, for example: + + {start_of_abc label="Alert" align="left"} + +See [Directives: Image]({{< relref "Directives-Image" >}}) for all +possible attributes. + +Additionally, the following attributes may be used: + +* split="_n_" + If set, ChordPro will attempt to split the generated image into individual + systems so longer scores can be put onto multiple pages. + As of 6.030 this is enabled by default. Use `split="0"` to keep the + score as a singlee image. + +* staffsep="_n_" + Add extra vertical space between the systems. + Only relevant when not splitting. + +## Formatting instructions (deprecated) The ABC data may be preceded by formatting instructions: @@ -77,10 +106,12 @@ The ABC data may be preceded by formatting instructions: * split If set, ChordPro will attempt to split the generated image into individual systems so longer scores can be put onto multiple pages. - As of 6.030 this is enabled by default. + As of 6.030 this is enabled by default. Use `split=0` to keep the + score as a single image. * staffsep=_n_ Add extra vertical space between the systems. + Only relevant when not splitting. # Directives: end_of_abc diff --git a/docs/content/Directives-env_svg.md b/docs/content/Directives-env_svg.md new file mode 100644 index 00000000..9c973f1a --- /dev/null +++ b/docs/content/Directives-env_svg.md @@ -0,0 +1,41 @@ +--- +title: "Directives: start_of_svg" +description: "Directives: start_of_svg" +--- + +# Directives: start_of_svg + +This directive indicates that the lines that follow define an image +described in [Scalable Vector Graphics](https://...). + +For example + + {start_of_svg} + + + + + + + + {end_of_svg} + +The result could look like: + +![]({{< asset "images/ex_svg1.png" >}}) + +## Attributes + +The SVG directive may contain the same formatting attributes as the +image directive, for example: + + {start_of_svg label="Alert" align="left"} + +See [Directives: Image]({{< relref "Directives-Image" >}}) for all +possible attributes. + +# Directives: end_of_svg + +This directive indicates the end of the svg section. + + diff --git a/docs/content/Directives-env_verse.md b/docs/content/Directives-env_verse.md index 885f34b9..1f105ee8 100644 --- a/docs/content/Directives-env_verse.md +++ b/docs/content/Directives-env_verse.md @@ -9,13 +9,18 @@ Abbreviation: `sov`. Specifies that the following lines form a verse of the song. -Lines that are outside any `start_of_…`/`end_of_…` part will also be interpreted as song lines in a verse, but it may be advantageous to explicitly specify it. +Lines that are outside any `start_of_…`/`end_of_…` part will also be +interpreted as song lines in a verse, but it may be advantageous to +explicitly specify it. This directive may include an optional label to identify the section. -For example:, {start_of_verse: Verse 1} +To be future proof it is advised to use _key_`=`_value_ syntax: + + {start_of_verse: label="Verse 1"} + The ChordPro reference implementation prints the label in the left margin, see [labels]({{< relref "ChordPro-Configuration-PDF#labels" >}}). diff --git a/docs/content/Directives-image.md b/docs/content/Directives-image.md index b8bea9d6..b879827e 100644 --- a/docs/content/Directives-image.md +++ b/docs/content/Directives-image.md @@ -7,7 +7,7 @@ description: "Directives: image" `{image:` `src=`*filename* _options_ `}` -Includes a bitmap image. +Includes an image. ## Simple use @@ -19,10 +19,20 @@ Includes a bitmap image. Specifies the name of the file containing the image. Supported file types may depend on the platforms and tools used, but PNG, JPG and GIF should always be valid. +Most likely, ABC and SVG will also be acceptable. The syntax of file names also depends on the platforms and -tools used. A simple file name like `"myimage.png"` should always +tools used. + +In general, a simple file name like `"myimage.png"` should always be acceptable. The image must then reside in the same directory as the -song, or in an `images` subdirectory of `CHORDPRO_LIB`. +song, or in an `images` subdirectory of one of the ChordPro resource +paths. + +A full (absolute) filename like `"/home/me/images/myimage.png"` or +`"C:\Users\Me\Documents\myimage.png"` is acceptable but not portable. + +Avoid relative filenames like `"images/myimage.png"`. They may or may not work, +do not count on it. Example: @@ -52,8 +62,14 @@ image is scaled to fit. Scales the image with the factor. This may be a floating point number, e.g. `0.2`, or a percentage, e.g. `20%`. +##### `align=`*aa* +Aligns the image on the page. The argument may be `"left"`, +`"center"` or `"right"`. +Default alignment is `"center"`. + ##### `center=`*tf* ##### `center` +(Deprecated) The image is horizontally centered on the page or column. If _tf_ equals `0`, the image is flushed left. @@ -161,3 +177,74 @@ This loads the image an assigns it asset id `im01`. The second occurrence places an image but instead of specifying the source of the image, it uses the asset with the given id. +## Assets from delegates + +Delegates, e.g. ABC, can produce assets. +Simply prepend the contents with a line `id=`_XXX_ to supply the id. +Note that you need an `{image}` directive to show it. + +# Inline images + +Images can also be placed inside texts using a special markup element +``. + +The img element takes several attributes. + +First, and most important, is the source of the image. This can be the +name of a file containing an image, e.g. + + src="image.jpg" + +Alternatively, an asset id can be used: + + id="im01" + +Finally, chord diagrams are images too: + + chord="Am7" + +Other attributes are: + +`width=`_NNN_ +: The desired width for the image. +The value can be a size (in points, `em` or `ex`) or a percentage. +The image is scaled if necessary. + +`height=`_NNN_ +: The desired height for the image. + +`dx=`_NNN_ +: A horizontal offset for the image, wrt. the current location in the text. +The value can be a size (in points, `em` or `ex`) or a percentage. + +`dy=`_NNN_ +: Same, but vertical. Positive amounts move up. + +Note the direction is opposite to the markup ``. + +`scale=`_NNN_ +: A scaling factor, to be applied _after_ width/height scaling. +The value may be expressed as a percentage. + +`align="left"` `align="right"` `align="center"` +: Align the image in the width given by the `w` attribute. + +`bbox=1` `bbox=0` +: If true, the actual bounding box of an object is used for placement. + +By default the bounding box is only used to obtain the width and height. + +This attribute has no effect on image objects. + +`w=`_NNN_ +: The advance width of the image. +This is the **actual** space occupied by the image. +If the image is wider it will overlap the text it is embedded in. + +Default advance is the image width plus horizontal offset. +This overrides the advance and may be zero. + +`h=`_NNN_ +: The advance height of the image. +Similar to `w` but vertically. + diff --git a/docs/content/Directives-new_song.md b/docs/content/Directives-new_song.md index c4258f59..58716a74 100644 --- a/docs/content/Directives-new_song.md +++ b/docs/content/Directives-new_song.md @@ -13,3 +13,11 @@ Examples: {new_song} {ns} + +Attributes may be added using [key/value pairs]({{< relref +"Key_Value_Pairs" >}}). + +* `toc=` _arg_ +Add the song title to the table of contents if _arg_. This is the +default case. Use `toc=no` to suppress this song from appearing +the in the table of contents. diff --git a/docs/content/Key_Value_Pairs.md b/docs/content/Key_Value_Pairs.md new file mode 100644 index 00000000..02e261c1 --- /dev/null +++ b/docs/content/Key_Value_Pairs.md @@ -0,0 +1,78 @@ +--- +title: Key/Value pairs +description: Key/Value pairs +--- + +# Key/Value pairs + +Many directives take additional attributes in the form of key/value +pairs. + +For example, + + label=Chorus + label="Chorus" + +Either will assign the value `"Chorus"` to attribute `label`. + +Using quotes is optional unless the value contains spaces. + + label="Verse 1" + +## To be or not to be + +Often an atribute takes a _logical_ value to enable or disable +something. + + diagram=no + +The attribute is considered enabled when the value is any of: + + 1 + on + true + +To disable, use: + + 0 + off + false + null + no + none + +In most cases, an empty value is also considered _false_, and everything else +not enumerated above is considered _true_. But best is to stick to the +values shown above. + +## Numeric values + +If an attribute requires a _numeric_ value, this can be an optionally +signed number, optionally followed by a fraction. + + 1 + -42 + 3.14 + +Degenerate cases like `.1` and `2.` are not recognized. + +Depending on the context a numeric value can have a _unit_, e.g. `60%` +or `1.4em`. + +| Unit | Value | +|------|----------------------------------------| +| `%` | Percentage, `60%` is the same as `0.6` | +| `em` | Fraction of the current font size | +| `ex` | Fraction of half the current font size | +| `pt` | Typographical point, `1/72` inch | +| `px` | Pixel, `1/96` inch, `0.75pt` | +| `in` | Inch, `2.54cm`, `72pt`, `96px` | +| `cm` | Centimeter, `10mm` | +| `mm` | Millimeter | +{ .table .table-striped .table-bordered .table-sm } + +Default for dimensions is points. For example, the following are +equivalent: + + width=515 + width=515pt diff --git a/docs/content/Links.md b/docs/content/Links.md index 51e94155..dfe0a574 100644 --- a/docs/content/Links.md +++ b/docs/content/Links.md @@ -22,6 +22,10 @@ description: "ChordPro related links" Mac OS X (Java-based native .app), and any platform that supports Java 1.5 (.jar file). +* [SongPress](https://www.skeed.it/songpress) + A simple but often effective editor with WYSIWYG capabilities. Runs + on Windows and Linux. + * [MobileSheetsPro](http://www.zubersoft.com/mobilesheets/) MobileSheetsPro is a music viewer for Android and Windows (and soon iOS). It is aimed at musicians to replace the music papers and books. It diff --git a/docs/content/Resources.md b/docs/content/Resources.md new file mode 100644 index 00000000..c85ac835 --- /dev/null +++ b/docs/content/Resources.md @@ -0,0 +1,95 @@ +--- +title: "Resources" +description: "Resources" +--- + +# Resources + +ChordPro makes use of _resources_, usually small files +that contain information like images or fonts. + +All resources can be accessed using either a full filename or a short +filename. + +An example of a full filename resource for a font is +`"/usr/share/fonts/liberation-sans/LiberationSans-Regular.ttf"`. +A resource specified with a full filename will always be used as such. + +When a resource is needed and it is not specified with a full +filename, ChordPro will search for it using a different strategies +depending on the resource. + +The first strategy is to look next to the song being processed. +If for example you are processing song file `blues/ballad.cho` +and this song requires resource `alert.svg`, +ChordPro will try `blues/alert.svg`. + +The second strategy is to perform a search for the resource in a number of +places, often referred to as _resource libraries_. + +The list of resource libraries is constructed as follows. + +* The directories specified as a +colon-separated (semi-colon on Windows systems) list in +the `CHORDPRO_LIB` environment setting. + +* The platform dependent user specific resource library. +On Linux systems +there is an environment variable `HOME` that is usually something like +`/home/`_username_. On Windows this may be something like +`C:\Users\`_username_ or `C:\Documents and Settings\`_username_. The +home directory is conventially specified with a tilde, `~`. +If the user's home directory contains a subdirectory +`.config/chordpro` this will be added to the resource libraries. + +* The ChordPro resource library that comes with the ChordPro installation. + +Running `chordpro --about` and the ChordPro GUI 'About' information +will show what libraries and locations will be used. + +## Configuration resources (presets) + +Preset configurations are searched for only in the resource libraries. + +## Tasks + +_Tasks are available in the GUI only._ + +Tasks are just like config files but they are also presented in the +GUI 'Tasks' menu. + +Tasks are searched for in all resource libraries that have a `tasks` +folder. + +The tasks are shown under a neatified form of the file that contains +the task. For example, a task file `Blue_Chords.prp` will be shown as +`Blue Chords`. You can change the title with a specially formatted +first line in the file: +`// chordpro task: `_desired title_ (for JSON files), and +`# chordpro task: `_desired title_ (for property files). + +## Images + +Images are searched for next to the song being processed, and, if not +found, in all resource libraries that have an `images` folder. + +## Fonts + +First, the font is looked up in all the directories specified in the +config setting `fontdir`. + +If not found, it is searched for in all resource libraries that have a +`fonts` folder. + +Finally, a left-over from early implementations, is the location of a +directory specified in the environment variable `FONTDIR`. + +## Includes + +_Note: This applies to the experimental `##include` feature and is not +related to config `"include"`._ + +Includes are searched for next to the song being processed, and, if not +found, in all resource libraries that have an `include` folder. + + diff --git a/lib/ChordPro.pm b/lib/ChordPro.pm index 9eab6e85..5c2128fa 100644 --- a/lib/ChordPro.pm +++ b/lib/ChordPro.pm @@ -4,8 +4,6 @@ use v5.26; package ChordPro; -use App::Packager; - use ChordPro::Utils; use ChordPro::Chords; use ChordPro::Output::Common; @@ -75,6 +73,18 @@ our $config; package ChordPro; +use ChordPro::Paths; +use Encode qw(decode decode_utf8 encode_utf8); + +sub import { + # Add private library. + my $lib = CP->privlib; + for ( @INC ) { + return if $_ eq $lib; + } + unshift( @INC, $lib ); +} + sub ::run { binmode(STDERR, ':utf8'); binmode(STDOUT, ':utf8'); @@ -138,9 +148,7 @@ sub chordpro { # No default if more than one input document. die("Please use \"--output\" to specify the output file name\n"); } - my $f = $ARGV[0]; - $f =~ s/\.\w+$/.pdf/; - $f .= ".pdf" if $f eq $ARGV[0]; + my $f = CP->sibling( $ARGV[0], ext => ".pdf" ); $options->{output} = $f; warn("Writing output to $f\n") if $options->{verbose}; } @@ -196,6 +204,7 @@ sub chordpro { $opts->{meta}->{__config} = \@cfg; } } + $opts->{generate} = $options->{generate}; # Wx runs on temp files, so pass real filename in. $opts->{filesource} = $options->{filesource}; $s->parse_file( $file, $opts ); @@ -276,19 +285,8 @@ sub chordpro { } sub ::dump { - use Data::Dumper qw(); - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Quotekeys = 0; - local $Data::Dumper::Deparse = 1; - local $Data::Dumper::Terse = 1; - local $Data::Dumper::Trailingcomma = 1; - local $Data::Dumper::Useperl = 1; - local $Data::Dumper::Useqq = 0; # I want unicode visible - - my $s = Data::Dumper::Dumper @_; - defined wantarray or warn $s; - return $s; + use ChordPro::Dumper; + ddp(@_); } ################ Options and Configuration ################ @@ -706,37 +704,7 @@ sub app_setup { } # Config files. - if ( -d "/etc" ) { # some *ux - $configs{sysconfig} = - File::Spec->catfile( "/", "etc", "$app_lc.json" ); - } - - if ( $ENV{XDG_CONFIG_HOME} && -d $ENV{XDG_CONFIG_HOME} ) { - $configs{userconfig} = - File::Spec->catfile( $ENV{XDG_CONFIG_HOME}, $app_lc, "$app_lc.json" ); - $ENV{CHORDPRO_LIB} ||= File::Spec->catfile( $ENV{XDG_CONFIG_HOME}, $app_lc); - } - elsif ( $ENV{HOME} && -d $ENV{HOME} ) { - my $dir = File::Spec->catfile( $ENV{HOME}, ".config" ); - if ( -d $dir ) { - $configs{userconfig} = - File::Spec->catfile( $dir, $app_lc, "$app_lc.json" ); - $ENV{CHORDPRO_LIB} ||= File::Spec->catfile( $dir, $app_lc ); - } - else { - $dir = File::Spec->catfile( $ENV{HOME}, ".$app_lc" ); - $configs{userconfig} = - File::Spec->catfile( $dir, "$app_lc.json" ); - $ENV{CHORDPRO_LIB} ||= $dir; - } - } - - if ( -s ".$app_lc.json" ) { - $configs{config} = ".$app_lc.json"; - } - else { - $configs{config} = "$app_lc.json"; - } + %configs = %{ CP->configs }; my $options = { @@ -902,8 +870,8 @@ sub app_setup { # Load Pod::Usage only if needed. require Pod::Usage; Pod::Usage->import; - my $f = $manual == 2 ? "pod/Config.pod" : "pod/ChordPro.pod"; - unshift( @_, -input => getresource($f) ); + my $f = $manual == 2 ? "Config.pod" : "ChordPro.pod"; + unshift( @_, -input => CP->findres( $f, class => "pod" ) ); &pod2usage; }; @@ -935,11 +903,13 @@ sub app_setup { for ( $clo->{$config} ) { if ( defined($_) ) { foreach my $c ( @$_ ) { + my $try = $c; # Check for resource names. - if ( $c !~ m;[/.]; ) { - $c = ::rsc_or_file( $c, "config" ); + if ( ! -r $try ) { + $try = CP->findcfg($c); } - die("$c: $!\n") unless -r $c; + die("$c: $!\n") unless $try && -r $try; + $c = $try; } next; } @@ -1049,19 +1019,17 @@ EndOfAbout exit $exit if defined $exit; } -use Cwd qw(realpath); +use List::Util qw(uniq); sub ::runtimeinfo { my $level = shift // "normal"; - + my $cp = ChordPro::Paths->get; my $fmt = " %-22.22s %-10s\n"; - my $fmtv = defined($Wx::VERSION) ? " %s version %-10s\n" : $fmt; - my $fmtvv = defined($Wx::VERSION) ? " %s %-10s\n" : $fmt; # Sometimes version numbers are localized... my $dd = sub { my $v = $_[0]; $v =~ s/,/./g; $v }; - my $msg = sprintf( $fmtv, "ChordPro core", $dd->($VERSION) ); + my $msg = sprintf( $fmt, "ChordPro core", $dd->($VERSION) ); $msg =~ s/core/reference/ if $::options->{reference}; if ( $VERSION =~ /_/ ) { $msg =~ s/\n$/ (Unsupported development snapshot)\n/; @@ -1074,31 +1042,50 @@ sub ::runtimeinfo { return $msg; } - $msg .= sprintf( $fmtv, "Perl", $^V ); - $msg =~ s/\n$/ ($^X)\n/; - if ( $App::Packager::PACKAGED ) { - my $p = App::Packager::Packager(); - $p .= " Packager" unless $p =~ /packager/i; - $msg .= sprintf( $fmtv, $p, $dd->(App::Packager::Version()) ); - } + $msg .= sprintf( $fmt, "Perl", $^V ); + $msg =~ s/\n$/sprintf(" (%s)\n", $cp->display($^X))/e; + if ( CP->packager ) { + $msg .= sprintf( $fmt, CP->packager." Packager", CP->packager_version ); + } + # Determine resource path. my @p; - if ( $ENV{CHORDPRO_LIB} ) { - $msg .= sprintf( $fmtvv, "CHORDPRO_LIB", $ENV{CHORDPRO_LIB} ); - @p = splitpath($ENV{CHORDPRO_LIB}); + my $tag = "CHORDPRO_LIB"; + if ( defined $ENV{CHORDPRO_LIB} ) { + for ( $cp->path($ENV{CHORDPRO_LIB}) ) { + $msg .= sprintf( $fmt, $tag, $cp->display($_) ); + $tag = ""; + push( @p, $cp->display($_) ); + } } - push( @p, realpath( App::Packager::GetResourcePath() ) ); - my $tag = "Resource path"; - for ( @p ) { - $msg .= sprintf( $fmtvv, $tag, $_ ); + push( @p, map { $cp->display($_) } @{ $cp->resdirs } ); + $tag = "Resource path"; + for ( uniq(@p) ) { + $msg .= sprintf( $fmt, $tag, $_ ); $tag = ""; } + eval { require ChordPro::Delegate::ABC; + my $x; + if ( ChordPro::Delegate::ABC::have_xs() ) { + $x = ChordPro::Delegate::ABC::packaged_qjs(); + $msg .= sprintf( $fmt, "ABC support", + $x->[0] . " (" . $x->[1] . ")" ); + } + elsif ( $x = findexe( "abc2svg", "silent" ) + || findexe( "abcnode", "silent" ) ) { + $msg .= sprintf( $fmt, "ABC support", $cp->display($x) ); + } + elsif ( $x = ChordPro::Delegate::ABC::packaged_qjs() ) { + $msg .= sprintf( $fmt, "ABC support", + $cp->display($x->[0]) . " (" . $cp->display($x->[-1]) . ")" ); + } + }; my $vv = sub { my ( $mod ) = @_; no strict 'refs'; - $msg .= sprintf( $fmtv, $mod, $dd->(${${"${mod}::"}{VERSION}}) ); + $msg .= sprintf( $fmt, $mod, $dd->(${${"${mod}::"}{VERSION}}) ); return unless $level eq "extensive"; chomp($msg); my $pm = $mod =~ s;::;/;gr . ".pm"; @@ -1117,8 +1104,8 @@ sub ::runtimeinfo { $msg .= "\nModules and libraries:\n"; if ( defined $Wx::VERSION ) { no strict 'subs'; - $msg .= sprintf( $fmtv, "wxPerl", $dd->($Wx::VERSION) ); - $msg .= sprintf( $fmtv, "wxWidgets", $dd->(Wx::wxVERSION) ); + $msg .= sprintf( $fmt, "wxPerl", $dd->($Wx::VERSION) ); + $msg .= sprintf( $fmt, "wxWidgets", $dd->(Wx::wxVERSION) ); } local $SIG{__WARN__} = sub {}; @@ -1130,7 +1117,7 @@ sub ::runtimeinfo { }; eval { require HarfBuzz::Shaper; $vv->("HarfBuzz::Shaper"); - $msg .= sprintf( $fmtv, "HarfBuzz library", $dd->(HarfBuzz::Shaper::hb_version_string()) ); + $msg .= sprintf( $fmt, "HarfBuzz library", $dd->(HarfBuzz::Shaper::hb_version_string()) ); }; $vv->("File::LoadLines"); eval { require PDF::Builder; @@ -1138,6 +1125,9 @@ sub ::runtimeinfo { }; eval { require PDF::API2; $vv->("PDF::API2"); + eval { require PDF::API2::XS; + $vv->("PDF::API2::XS"); + }; }; eval { require SVGPDF; $vv->("SVGPDF"); @@ -1145,6 +1135,9 @@ sub ::runtimeinfo { eval { require Font::TTF; $vv->("Font::TTF"); }; + eval { require JavaScript::QuickJS; + $vv->("JavaScript::QuickJS"); + }; return $msg; } @@ -1240,40 +1233,6 @@ EndOfUsage exit $exit if defined $exit; } -################ Resources ################ - -use Encode qw(decode decode_utf8 encode_utf8); - -sub ::rsc_or_file { - my ( $c, $cfg ) = @_; - my $f = $c; - $cfg .= "/" if $cfg; - - # Check for resource names. - if ( $f !~ m;[/.]; ) { - if ( $c =~ /^(.+):(.*)/ ) { - $f = $cfg . lc($1) . "/" . lc($2) . ".json"; - } - else { - $f = $cfg . lc($c) . ".json"; - } - } - if ( $ENV{CHORDPRO_LIB} ) { - my @libs = splitpath($ENV{CHORDPRO_LIB}); - foreach my $lib ( @libs ) { - $lib = expand_tilde($lib); - warn("RSC1: $lib/$f\n") if $options->{debug}; - return $lib . "/" . $f if -r $lib . "/" . $f; - } - } - - warn("RSC3: $f\n") if $options->{debug}; - my $t = getresource($f); - return defined($t) ? $t : $c; -} - -use lib ( grep { defined } getresource("CPAN") ); - =head1 FONTS There are two ways to specify fonts: with a font filename, and a diff --git a/lib/ChordPro/A2Crd.pm b/lib/ChordPro/A2Crd.pm index 0ff7f03b..bfa9ea3d 100644 --- a/lib/ChordPro/A2Crd.pm +++ b/lib/ChordPro/A2Crd.pm @@ -4,9 +4,8 @@ use v5.26; package ChordPro::A2Crd; -use App::Packager; - use ChordPro::Version; +use ChordPro::Paths; use ChordPro::Chords; our $VERSION = $ChordPro::Version::VERSION; @@ -92,6 +91,7 @@ use ChordPro::Config; use File::LoadLines; use Encode qw(decode decode_utf8 encode_utf8); +my $local_debug; # API: Main entry point. sub a2crd { @@ -100,6 +100,7 @@ sub a2crd { # One configurator to bind them all. $config = ChordPro::Config::configurator({}); + $local_debug = $config->{debug}->{a2crd}; # Process input. my $lines = $opts->{lines} @@ -400,7 +401,6 @@ sub decode_fingering sub classify_pct_chords { my ( $line ) = @_; my $lc_line = lc($line) ; - my $local_debug=0 ; return 'C' if $line =~ /^\s*\[.+?\]/; # comment return 'C' if $line =~ /^\s*\#.+?/; # comment @@ -540,7 +540,6 @@ my $infer_titles; sub maplines { my ( $map, $lines ) = @_; my @out; - my $local_debug=0 ; $infer_titles //= $::config->{a2crd}->{'infer-titles'}; # Preamble. @@ -610,7 +609,7 @@ sub maplines { while(1) { $line = shift @$lines ; die "Malformed input, {sot} has no matching {eot}" if ! $line ; - $map = s/.// ; + $map =~ s/.// ; push( @out, $line); last if $line =~ /{eot}/ ; } @@ -830,34 +829,9 @@ sub app_setup { } # Config files. - my $app_lc = lc("ChordPro"); # common config - if ( -d "/etc" ) { # some *ux - $configs{sysconfig} = - File::Spec->catfile( "/", "etc", "$app_lc.json" ); - } - - my $e = $ENV{CHORDIIRC} || $ENV{CHORDRC}; - if ( $ENV{HOME} && -d $ENV{HOME} ) { - if ( -d File::Spec->catfile( $ENV{HOME}, ".config" ) ) { - $configs{userconfig} = - File::Spec->catfile( $ENV{HOME}, ".config", $app_lc, "$app_lc.json" ); - } - else { - $configs{userconfig} = - File::Spec->catfile( $ENV{HOME}, ".$app_lc", "$app_lc.json" ); - } - $e ||= File::Spec->catfile( $ENV{HOME}, ".chordrc" ); - } - $e ||= "/chordrc"; # Windows, most likely - $configs{legacyconfig} = $e if -s $e && -r _; - - if ( -s ".$app_lc.json" ) { - $configs{config} = ".$app_lc.json"; - } - else { - $configs{config} = "$app_lc.json"; - } + %configs = %{ CP->configs }; + my $app_lc = lc("ChordPro"); # common config my $options = { verbose => 0, # verbose processing @@ -918,7 +892,7 @@ sub app_setup { require Pod::Usage; Pod::Usage->import; my $f = "pod/A2Crd.pod"; - unshift( @_, -input => getresource($f) ); + unshift( @_, -input => CP->findres($f) ); &pod2usage; }; @@ -949,11 +923,12 @@ sub app_setup { for ( $clo->{$config} ) { if ( defined($_) ) { foreach my $c ( @$_ ) { + my $try = $c; # Check for resource names. - if ( ! -r $c && $c !~ m;[/.]; ) { - $c = ::rsc_or_file( $c, "config" ); + if ( ! -r $try ) { + $try = CP->findcfg($c); } - die("$c: $!\n") unless -r $c; + die("$c: $!\n") unless -r $try; } next; } diff --git a/lib/ChordPro/Chords/Parser.pm b/lib/ChordPro/Chords/Parser.pm index fe1b411e..0b96b7ef 100644 --- a/lib/ChordPro/Chords/Parser.pm +++ b/lib/ChordPro/Chords/Parser.pm @@ -543,6 +543,7 @@ sub new { my $self = bless { chord_cache => {} } => $pkg; $self->{system} = "nashville"; $self->{target} = 'ChordPro::Chord::Nashville'; + $self->{intervals} = 12; warn("Chords: Created parser for ", $self->{system}, "\n") if $::options->{verbose} && $::options->{verbose} > 1; return $parsers{$self->{system}} = $self; @@ -655,6 +656,7 @@ sub new { my $self = bless { chord_cache => {} } => $pkg; $self->{system} = "roman"; $self->{target} = 'ChordPro::Chord::Roman'; + $self->{intervals} = 12; warn("Chords: Created parser for ", $self->{system}, "\n") if $::options->{verbose} && $::options->{verbose} > 1; return $parsers{$self->{system}} = $self; @@ -1003,7 +1005,9 @@ sub transcode ( $self, $xcode, $key_ord = 0 ) { my $p = $self->{parser}->get_parser($xcode); die("OOPS ", $p->{system}, " $xcode") unless $p->{system} eq $xcode; $info->{parser} = $p; - $info->{root_ord} -= $key_ord if $key_ord && $p->movable; + if ( $key_ord && $p->movable ) { + $info->{root_ord} -= $key_ord % $p->intervals; + } # $info->{$_} = $p->{$_} for qw( ns_tbl nf_tbl ns_canon nf_canon ); $info->{root_canon} = $info->{root} = $p->root_canon( $info->{root_ord}, @@ -1014,7 +1018,9 @@ sub transcode ( $self, $xcode, $key_ord = 0 ) { $info->{qual_canon} = $info->{qual} = ""; } if ( $self->{bass} && $self->{bass} ne "" ) { - $info->{bass_ord} -= $key_ord if $key_ord && $p->movable; + if ( $key_ord && $p->movable ) { + $info->{bass_ord} -= $key_ord % $p->intervals; + } $info->{bass_canon} = $info->{bass} = $p->root_canon( $info->{bass_ord}, $info->{bass_mod} >= 0 ); } diff --git a/lib/ChordPro/Config.pm b/lib/ChordPro/Config.pm index 4499bb22..541422d2 100644 --- a/lib/ChordPro/Config.pm +++ b/lib/ChordPro/Config.pm @@ -13,8 +13,8 @@ use Carp; use feature qw( signatures ); no warnings "experimental::signatures"; -use App::Packager; use ChordPro; +use ChordPro::Paths; use ChordPro::Utils; use File::LoadLines; use File::Spec; @@ -55,6 +55,7 @@ sub configurator ( $opts = undef ) { unless ( $opts ) { my $cfg = $pp->decode( default_config() ); $config = $cfg; + config_split_fc_aliases($cfg); $options = { verbose => 0 }; process_config( $cfg, "" ); $cfg->{settings}->{lineinfo} = 0; @@ -71,6 +72,9 @@ sub configurator ( $opts = undef ) { warn("Reading: \n") if $verbose > 1; my $cfg = $pp->decode( default_config() ); + # This is easier than splitting out manually :) + config_split_fc_aliases($cfg); + # Default first. @cfg = prep_configs( $cfg, "" ); # Bubble default config to be the first. @@ -308,7 +312,7 @@ sub prep_configs ( $cfg, $src ) { foreach my $c ( @{ $cfg->{include} } ) { # Check for resource names. if ( $c !~ m;[/.]; ) { - $c = ::rsc_or_file( $c, "config" ); + $c = CP->findcfg($c); } elsif ( $dir ne "" && !File::Spec->file_name_is_absolute($c) ) { @@ -361,6 +365,27 @@ sub process_config ( $cfg, $file ) { $cfg->{_chords} = delete $cfg->{chords}; ChordPro::Chords::pop_parser(); } + config_split_fc_aliases($cfg); +} + +sub config_split_fc_aliases ( $cfg ) { + # Split fontconfig aliases into individual entries. + if ( $cfg->{pdf}->{fontconfig} ) { + # Orig. + my $fc = $cfg->{pdf}->{fontconfig}; + # Since we're going to delete/insert keys, we need a copy. + my %fc = %$fc; + while ( my($k,$v) = each(%fc) ) { + # Split on comma. + my @k = split( /\s*,\s*/, $k ); + if ( @k > 1 ) { + # We have aliases. Delete the original. + delete( $fc->{$k} ); + # And insert individual entries. + $fc->{$_} = $v for @k; + } + } + } } sub config_final ( $delta ) { @@ -402,7 +427,7 @@ sub config_default () { # Config in properties format. -sub cfg2props ( $o, $path ) { +sub cfg2props ( $o, $path = "" ) { $path //= ""; my $ret = ""; if ( !defined $o ) { @@ -848,7 +873,7 @@ sub get_context () { } # For testing -use base qw(Exporter); +use Exporter 'import'; our @EXPORT = qw( _c ); sub _c ( @args ) { $::config->gps(@args) } @@ -915,6 +940,10 @@ sub default_config () { // Substitute Unicode sharp/flats in chord names. // Will fallback to ChordProSymbols the font doesn't have the glyphs. "truesf" : false, + // Amount of indent for wrapped lines. Actual indent is the stringwidth. + "wrapindent" : "x", + // Flow text. Do not use. + "flowtext" : false, }, // Metadata. @@ -938,10 +967,13 @@ sub default_config () { // Globally defined (added) meta data, // This is explicitly NOT intended for the metadata items above. "meta" : { + // Do not remove or change this one. + "_configversion" : [ "6.031" ], }, // Assets. - "assets" : {}, + "assets" : { + }, // Dates. Format is a strftime template. "dates" : { @@ -1028,7 +1060,7 @@ sub default_config () { // "all": all chords used. // "user": only prints user defined chords. // "sorted": order the chords by key. - // "suppress": a series of chord (names) thet will not generate + // "suppress": a series of chord (names) that will not generate // diagrams, e.g. if they are considered trivial. // Note: The type of diagram (string or keyboard) is determined // by the value of "instrument.type". @@ -1087,25 +1119,17 @@ sub default_config () { "type" : "image", "module" : "ABC", "handler" : "abc2svg", + // No longer used -- ./default.abc will always be used if present "config" : "default", // or "none", or "myformat.fmt" - // The preamble is a list of lines inserted before the ABC data. + // The preamble is a list of lines inserted before the ABC data, + // and after the delegate supplied preamble. // DO NOT MODIFY unless you know what you are doing! "preamble" : [ - // Get rid of as much space as possible. - "%%topspace 0", - "%%titlespace 0", - "%%musicspace 0", - "%%composerspace 0", - "%%infospace 0", - "%%textspace 0", - "%%leftmargin 0cm", - "%%rightmargin 0cm", - "%%staffsep 0", // Use ChordPro fonts for lyrics and chords. "%%textfont pdf.fonts.text", "%%gchordfont pdf.fonts.chord", ], - "preprocess" : { "abc" : [], "svg" : [] }, + "preprocess" : { "abc" : [] }, "omit" : false, }, "ly" : { @@ -1121,6 +1145,12 @@ sub default_config () { ], "omit" : false, }, + "svg" : { + "type" : "image", + "module" : "SVG", + "handler" : "svg2svg", + "omit" : false, + }, }, // Definitions for PDF output. @@ -1379,11 +1409,7 @@ sub default_config () { "fontdir" : [], "fontconfig" : { - // alternatives: regular r normal - // alternatives: bold b strong - // alternatives: italic i oblique o emphasis - // alternatives: bolditalic bi italicbold ib boldoblique bo obliquebold ob - "times" : { + "times, serif" : { "" : "Times-Roman", "bold" : "Times-Bold", "italic" : "Times-Italic", @@ -1392,10 +1418,17 @@ sub default_config () { "helvetica" : { "" : "Helvetica", "bold" : "Helvetica-Bold", + // Only helvetica uses oblique, use italic for all other fonts "oblique" : "Helvetica-Oblique", "boldoblique" : "Helvetica-BoldOblique", }, - "courier" : { + "sans, sans-serif" : { + "" : "Helvetica", + "bold" : "Helvetica-Bold", + "italic" : "Helvetica-Oblique", + "bolditalic" : "Helvetica-BoldOblique", + }, + "courier, mono, monospace" : { "" : "Courier", "bold" : "Courier-Bold", "italic" : "Courier-Italic", @@ -1542,6 +1575,8 @@ sub default_config () { "quote" : false, }, }, + // Retain # comments -- we'll output them. + "comments" : "retain", }, // Settings for HTML backend. @@ -1608,25 +1643,29 @@ sub default_config () { // For (debugging (internal use only)). "debug" : { - "chords" : 0, - "config" : 0, - "echo" : 0, - "fonts" : 0, - "images" : 0, - "layout" : 0, - "meta" : 0, - "mma" : 0, - "spacing" : 0, - "song" : 0, - "songfull" : 0, - "csv" : 0, - "abc" : 0, - "ly" : 0, - "svg" : 0, + "a2crd" : 0, + "assets" : 0, + "chords" : 0, + "config" : 0, + "echo" : 0, + "fonts" : 0, + "images" : 0, + "layout" : 0, + "meta" : 0, + "mma" : 0, + "paths" : 0, + "spacing" : 0, + "song" : 0, + "songfull" : 0, + "ops" : 0, + "csv" : 0, + "abc" : 0, + "ly" : 0, + "svg" : 0, // For temporary use. - "x1" : 0, - "x2" : 0, - "x3" : 0, + "x1" : 0, + "x2" : 0, + "x3" : 0, }, } diff --git a/lib/ChordPro/Delegate/ABC.pm b/lib/ChordPro/Delegate/ABC.pm index 5b2148b5..77550637 100644 --- a/lib/ChordPro/Delegate/ABC.pm +++ b/lib/ChordPro/Delegate/ABC.pm @@ -1,5 +1,8 @@ #!/usr/bin/perl +use v5.26; +use utf8; + package main; our $config; @@ -9,6 +12,8 @@ package ChordPro::Delegate::ABC; use strict; use warnings; +use feature qw( signatures ); +no warnings "experimental::signatures"; use utf8; use Carp; use File::Spec; @@ -17,6 +22,7 @@ use File::LoadLines; use feature 'state'; use Encode 'decode_utf8'; +use ChordPro::Paths; use ChordPro::Utils; use Text::ParseWords qw(shellwords); @@ -25,83 +31,156 @@ sub DEBUG() { $config->{debug}->{abc} } # ABC processing using abc2svg and custom SVG processor. my $abc2svg; -my $embedded; -sub abc2svg_qjs { - my ( $s, $pw, $elt ) = @_; +# Default entry point. - # Embedded QuickJS. +sub abc2svg( $s, $pw, $elt ) { - my $dir = ::rsc_or_file("abc/"); - my $x; - if ( -x "$dir/qjs" ) { - $x = "$dir/qjs"; + if ( DEBUG() ) { + warn( sprintf( "ABC: abc2svg (tool = %s)\n", + ref($abc2svg) + ? $abc2svg->[0] + : ( $abc2svg) // "" ) ); } - elsif ( is_msw() and -s "$dir/qjs.exe" ) { - $x = "$dir/qjs.exe"; + + state $cfg_checked; + unless ( $cfg_checked++ ) { + if ( ($config->{delegates}{abc}{config} // "default") ne "default" ) { + warn("ABC: delegates.abc.config is no longer used.\n"); + warn("ABC: Config \"default.abc\" will be loaded instead.\n") + if -s "default.abc"; + } } - if ( $x ) { - $abc2svg = [ $x, "--std", "$dir/chordproabc.js", "$dir/abc2svg/" ]; + + # Try to find a way to run the abv2svg javascript code. + # We support the following strategies, in order: + # 1. Embedded JavaScript::QuickJS module. + # 2. A program 'abc2svg' in PATH, that writes the SVG to standard out + # (In newer installs it is called 'abcnode' so we try that as well.) + # 3. The QuickJS program, either in PATH or in our 'abc' resource + # directory. In this case, packaged abc2svg is used. + # The actual command is stored in $abc2svg and retained across calls. + # + # Note we do not use 'node' since it is hard to instruct it not to use + # global data. + + return abc2svg_qjs( $s, $pw, $elt ) if have_xs(); + + # If packaged, do not use external tools. + unless ( CP->packager ) { + + # First, try native program. + unless ( $abc2svg ) { + $abc2svg = findexe( "abc2svg", "silent" ); + $abc2svg = [ $abc2svg ] if $abc2svg; + } + unless ( $abc2svg ) { + $abc2svg = findexe( "abcnode", "silent" ); + $abc2svg = [ $abc2svg ] if $abc2svg; + } + + # We know what to do. + if ( $abc2svg ) { + return _abc2svg( $s, $pw, $elt ); + } } - $embedded = 1; - return _abc2svg( $abc2svg, $s, $pw, $elt ); + + # Try (optionally packaged) QuickJS with packaged abc2svg. + return abc2svg_qjs( $s, $pw, $elt ); } -sub abc2svg { - my ( $s, $pw, $elt ) = @_; +# Alternative entry point that always uses QuickJS only. - # Native. - unless ( $abc2svg ) { - $embedded = 0; - $abc2svg = findexe( "abc2svg", "silent" ); - } +sub packaged_qjs() { - # Try node. - unless ( $abc2svg ) { - my $x; - if ( $x = findexe( "npx", "silent" ) - or is_msw() and $x = findexe( "npx.cmd", "silent" ) ) { - my $dir = ::rsc_or_file("abc2svg/");; - $abc2svg = [ $x, "$dir/abc2svg" ]; + # Only use ours. + my $dir = CP->findresdirs("abc")->[-1]; + + if ( have_xs() ) { + my $js = "$dir/abc2svg/abc2svg-1.js"; + my @js = loadlines( $js, { split => 0 } ); + if ( $js[-1] =~ /abc2svg.version="(.*?)";abc2svg.vdate="(.*?)"/ ) { + return [ "QuickJS_XS", "ABC2SVG version $1 of $2", $dir ]; } + return [ "QuickJS_XS", $dir ]; } - if ( $abc2svg ) { - return _abc2svg( $abc2svg, $s, $pw, $elt ); + my $qjs; + + # First, try packaged qjs. + if ( -x "${dir}/qjs" ) { + $qjs = "${dir}/qjs"; + } + elsif ( is_msw() and -s "${dir}/qjs.exe" ) { + $qjs = "${dir}/qjs.exe"; } - # Try (embedded) QuickJS. - return abc2svg_qjs( $s, $pw, $elt ); + # Else try to find an installed qjs. + else { + $qjs = CP->findexe("qjs"); + } + + # If so, check for packaged abc files. + if ( $qjs + && -s "${dir}/chordproabc.js" + && -s "${dir}/abc2svg/tohtml.js" ) { + return [ $qjs, "--std", "${dir}/chordproabc.js", "${dir}/abc2svg" ]; + } + return 0; } -sub _abc2svg { - my ( $abc2svg, $s, $pw, $elt ) = @_; +sub abc2svg_qjs( $s, $pw, $elt ) { - state $imgcnt = 0; - state $td = File::Temp::tempdir( CLEANUP => !$config->{debug}->{abc} ); - my $cfg = $config->{delegates}->{abc}; + $abc2svg //= packaged_qjs(); + + # This will bail out if we didn't find a suitable program. + return _abc2svg( $s, $pw, $elt ); +} + +# Internal handler. +sub _abc2svg( $s, $pw, $elt ) { + + # Bail out if we don't have a suitable program. unless ( $abc2svg ) { - warn("Error in ABC embedding: need 'abc2svg' tool.\n"); + warn("Error in ABC embedding: no 'abc2svg' or 'qjs' program found.\n"); return; } + state $td = File::Temp::tempdir( CLEANUP => !$config->{debug}->{abc} ); + my $cfg = $config->{delegates}->{abc}; + + warn("ABC: Using config \"default.abc\".\n") + if !have_xs() && -s "default.abc"; my $prep = make_preprocessor( $cfg->{preprocess} ); + # Prepare names for temporary files. + state $imgcnt = 0; $imgcnt++; my $src = File::Spec->catfile( $td, "tmp${imgcnt}.abc" ); my $svg = File::Spec->catfile( $td, "tmp${imgcnt}.svg" ); my $out = File::Spec->catfile( $td, "tmp${imgcnt}.out" ); my $err = File::Spec->catfile( $td, "tmp${imgcnt}.err" ); - my $fd; - unless ( open( $fd, '>:utf8', $src ) ) { - warn("Error in ABC embedding: $src: $!\n"); - return; - } - - - my @preamble = @{ $cfg->{preamble} }; + # Get rid of as much space as possible. + # Jean-François Moine: + # If you have both "%%stretchstaff 1" and "%%trimsvg 1", and + # "%%stretchlast 0", only the final line is shorter. + # Otherwise, you can have "%%trimsvg 1" as the last line of the tune. + my @preamble = + ( "%%topspace 0", + "%%titlespace 0", + "%%musicspace 0", + "%%composerspace 0", + "%%infospace 0", + "%%textspace 0", + "%%leftmargin 0cm", + "%%rightmargin 0cm", + "%%stretchstaff 1", + "%%stretchlast 0", + "%%trimsvg 1", + "%%staffsep 0", + @{ $cfg->{preamble} } ); for ( keys(%{$elt->{opts}}) ) { @@ -125,30 +204,37 @@ sub _abc2svg { @data = ( "X:1", @pre ); @pre = (); } - my $kv = { %$elt }; + my $kv = {}; $kv = parse_kv( @pre ) if @pre; - $kv->{split} = 1; + $kv = { %$kv, %{$elt->{opts}} }; + $kv->{split} //= 1; # less overhead. really. $kv->{scale} ||= 1; + $kv->{align} //= ($kv->{center}//0) ? "center" : "left"; if ( $kv->{width} ) { $pw = $kv->{width}; } unshift( @preamble, grep { /^%%/ } @pre, - $pw ? ( "%%pagewidth " . $pw . "px" ) : (), - "%%leftmargin 0cm", - "%%rightmargin 0cm", + $pw ? sprintf("%%%%pagewidth %dpx", $pw) : (), ); + # Create the temp file for the ABC source. + my $fd; + unless ( open( $fd, '>:utf8', $src ) ) { + warn("Error in ABC embedding: $src: $!\n"); + return; + } + # Copy. We assume the user knows how to write ABC. for ( @preamble ) { print $fd $_, "\n"; - warn($_, "\n") if DEBUG; + warn($_, "\n") if DEBUG > 1; } for ( @data ) { $prep->{abc}->($_) if $prep->{abc}; print $fd $_, "\n"; - warn($_, "\n") if DEBUG; + warn($_, "\n") if DEBUG > 1; } unless ( close($fd) ) { @@ -156,27 +242,89 @@ sub _abc2svg { return; } - my @cmd = ref($abc2svg) ? ( @$abc2svg ) : ( $abc2svg ); + my @cmd = @$abc2svg; my @lines; my $ret; - push( @cmd, "toxhtml.js", $src ); - if ( $embedded ) { - splice( @cmd, 3, 0, $out ); # insert output name. + if ( $cmd[0] eq "QuickJS_XS" ) { + my $js = JavaScript::QuickJS->new; + my $base = $cmd[-1] . "/abc2svg"; + $js->set_module_base($base); + + my $abc2svg = + { + print => sub { push( @lines, split(/\n/, $_) ) for @_ }, + printErr => sub { print STDERR @_ }, + quit => sub { exit 66 }, + readFile => sub { slurp($_[0]) }, + get_mtime => sub { + my @stat = stat($_[0]); + return @stat ? 1000*$stat[9] : undef; + }, + loadjs => sub { + my ( $fn, $relay, $onerror ) = @_; + if ( -s -r "$base/$fn" ) { + $js->eval(slurp("$base/$_[0]")); + } + elsif ( $onerror ) { + $onerror->(); + } + else { + warn( qq{loadjs("$fn"): $!\n} ); + } + }, + }; + + $js->set_globals + ( args => [ $src ], + load => sub { $js->eval(slurp("$base/$_[0]")) }, + abc2svg => $abc2svg, + abc => '', # global for 'toxxx.js' + ); + + warn( "+ QuickJS[", CP->display($base), "] $src\n") if DEBUG; + eval { + $js->eval( slurp("$base/abc2svg-1.js") ); + if ( -r "$base/../cmd.js" ) { + warn(" QuickJS using ", CP->display("$base/../cmd.js"), "\n" ) + if DEBUG; + $js->eval( slurp("$base/../cmd.js") ); + } + else { + $js->eval( slurp("$base/cmdline.js") ); + } + $js->eval( slurp("$base/tohtml.js") ); + $js->eval( qq{abc_cmd("ChordPro", args, "QuickJS (embedded)")} ); + }; + warn($@) if $@; + undef $js; + + } + + elsif ( $cmd[0] =~ /qjs(?:\.\w+)?$/ ) { + + # Packaged. + push( @cmd, $out, $src ); if ( DEBUG ) { warn( "+ @cmd\n" ); $ENV{CHORDPRO_ABC_DEBUG} = 1; } + + # Run the command. $ret = eval { sys( @cmd ) }; + + # Load data. @lines = loadlines($out) } - elsif ( ! main->can("OnInit") ) { + # Not packaged. Check for Wx on Windows since we cannot redirect STD***. + elsif ( !is_wx() && !is_msw() ) { + push( @cmd, $src ); warn( "+ @cmd\n" ) if DEBUG; - # Command line use. Redirect stdout/err. + # Setup redirection for STDOUT/ERR. my ( $oldout, $olderr ); open( $oldout, ">&STDOUT" ) or die "Can't dup STDOUT: $!"; @@ -191,27 +339,22 @@ sub _abc2svg { select STDERR; $| = 1; # make unbuffered select STDOUT; $| = 1; # make unbuffered + # Run the command. $ret = eval { sys(@cmd) }; + # Reconnect STDOUT/ERR. open(STDOUT, ">&", $oldout) or die "Can't dup OLDOUT: $!"; open(STDERR, ">&", $olderr) or die "Can't dup OLDERR: $!"; select STDERR; $| = 1; # make unbuffered - if ( -s $err ) { - open( $fd, '<:utf8', $err ); - while ( <$fd> ) { - warn("ABC: $_"); - } - close($fd); - } - # Load data. @lines = loadlines($out); } else { + push( @cmd, $src ); if ( 0 ) { # This seemed a good idea but unfortunately Wx has problems # returning the UTF8 data correctly. Non-ASCII characters are @@ -238,42 +381,83 @@ sub _abc2svg { warn("Error in ABC embedding (no output?)\n"); return; } - warn("SVG: ", scalar(@lines), " lines (raw)\n") if DEBUG(); + warn("SVG: ", scalar(@lines), " lines (raw)\n") if DEBUG > 1; - open( $fd, '>:utf8', $svg ); + # Postprocess the SVG data. my $copy = 0; - print $fd ("
\n"); + @data = (); my $lines = 1; while ( @lines ) { $_ = shift(@lines); if ( /^\n"); - close($fd); - warn("SVG: ", 1+$lines, " lines (", -s $svg, " bytes)\n") if DEBUG(); - my @res; - push( @res, - { type => "svg", - uri => $svg, - opts => { center => $kv->{center}, - scale => $kv->{scale}, - split => $kv->{split}, - sep => $kv->{staffsep}, - } } ); - - return \@res; + if ( @data ) { + unshift( @data, "
" ); + push( @data, "
" ); + } + + if ( DEBUG ) { + open( $fd, '>:utf8', $svg ); + print( $fd $_, "\n" ) for @data; + close($fd); + warn("SVG: ", 1+$lines, " lines (", -s $svg, " bytes)\n") if DEBUG > 1; + } + + return + { type => "image", + line => $elt->{line}, + subtype => "svg", + data => \@data, + opts => { maybe id => $kv->{id}, + maybe align => $kv->{align}, + maybe scale => $kv->{scale}, + maybe split => $kv->{split}, + maybe spread => $kv->{spread}, + maybe sep => $kv->{staffsep}, + } }; +} + +sub have_xs { + local $SIG{__WARN__} = sub {}; + state $ok; + $ok //= eval { require JavaScript::QuickJS }; } -sub abc2image { +sub slurp { + my ( $fn ) = @_; + my $opts = { split => 0, fail => "soft" }; + my $data = loadlines( $fn, $opts ); + warn("LOAD($fn): ", $opts->{error}, "\n") + unless defined $data || $fn eq "default.abc"; + $data; +} + +sub abc2image( $s, $pw, $elt ) { + + croak("ABC: Please remove handler \"abc2image\" from your ABC delegates config"); + +} - croak("ABC: Please adjust your config to use ABC handler \"abc2svg\" instead of \"abc2image\""); +# Pre-scan. +sub options( $data ) { + my @pre; + my @data = @$data; + while ( @data ) { + last if $data[0] =~ /^([A-Z]:|\%)/; + push( @pre, shift(@data) ); + } + @pre = () if @pre && !@data; # no data found + my $kv = {}; + $kv = parse_kv( @pre ) if @pre; + $kv->{align} //= ($kv->{center}//0) ? "center" : "left"; + $kv; } 1; diff --git a/lib/ChordPro/Delegate/Lilypond.pm b/lib/ChordPro/Delegate/Lilypond.pm index ceed67d4..2f09df92 100644 --- a/lib/ChordPro/Delegate/Lilypond.pm +++ b/lib/ChordPro/Delegate/Lilypond.pm @@ -9,6 +9,8 @@ package ChordPro::Delegate::Lilypond; use strict; use warnings; +use feature qw( signatures ); +no warnings "experimental::signatures"; use utf8; use File::Spec; use File::Temp (); @@ -20,8 +22,7 @@ use Text::ParseWords qw(shellwords); sub DEBUG() { $config->{debug}->{ly} } -sub ly2svg { - my ( $s, $pw, $elt ) = @_; +sub ly2svg( $s, $pw, $elt ) { state $imgcnt = 0; state $td = File::Temp::tempdir( CLEANUP => !$config->{debug}->{ly} ); @@ -80,6 +81,9 @@ sub ly2svg { my $kv = { %$elt }; $kv = parse_kv( @pre ) if @pre; + $kv->{scale} ||= 1; + $kv->{align} //= ($kv->{center}//0) ? "center" : "left"; + # Copy. We assume the user knows how to write LilyPond. for ( @data ) { print $fd $_, "\n"; @@ -116,207 +120,37 @@ sub ly2svg { return; } - warn("SVG: ", -s $svg, " bytes\n"); - my @res; - push( @res, - { type => "svg", + warn("SVG: ", -s $svg, " bytes\n") if $config->{debug}->{ly}; + return + { type => "image", + line => $elt->{line}, + subtype => "svg", uri => "$im1.cropped.svg", - opts => { center => $kv->{center}, - scale => $kv->{scale}, - split => $kv->{split}, - } } ); - - return \@res; - + opts => { maybe id => $kv->{id}, + maybe align => $kv->{align}, + maybe scale => $kv->{scale}, + maybe spread => $kv->{spread}, + } }; } -sub ly2image { - my ( $s, $pw, $elt ) = @_; - +sub ly2image( $s, $pw, $elt ) { croak("Lilypond: Please adjust your delegate config to use handler \"ly2svg\" instead of \"ly2image\""); +} - state $imgcnt = 0; - state $td = File::Temp::tempdir( CLEANUP => !$config->{debug}->{ly} ); - - $imgcnt++; - my $src = File::Spec->catfile( $td, "tmp${imgcnt}.ly" ); - my $img = File::Spec->catfile( $td, "tmp${imgcnt}.png" ); - - my $fd; - unless ( open( $fd, '>:utf8', $src ) ) { - warn("Error in Lilypond embedding: $src: $!\n"); - return; - } +# Pre-scan. +sub options( $data ) { - my $need_version = 1; my @pre; - for ( keys(%{$elt->{opts}}) ) { - - if ( $_ eq "version" ) { - push( @pre, "\\version \"", $elt->{opts}->{$_}, "\"" ); - warn ( "\\version \"", $elt->{opts}->{$_}, "\"\n" ) if DEBUG; - $need_version = 0; - } - else { - push( @pre, '%%'.$_." ".$elt->{opts}->{$_} ); - warn('%%'.$_." ".$elt->{opts}->{$_}."\n") if DEBUG; - } - } - - for ( @{ $config->{delegates}->{ly}->{preamble} } ) { - push( @pre, $_ ); - warn( "$_\n") if DEBUG; - $need_version = 0 if /^\\version\s+/; - } - - if ( $need_version ) { - my $v = "2.21.0"; - unshift( @pre, "\\version \"$v\"", - "\\header { tagline = ##f }" ); - warn("ly: no \\version seen, assuming \"$v\"\n"); - } - printf $fd "$_\n" for @pre; - - @pre = (); - my @data = @{$elt->{data}}; - while ( @data ) { - $_ = shift(@data); - unshift( @data, $_ ), last if /^[%\\]/; # LP data - push( @pre, $_ ); + my @data = @$data; + while ( @$data ) { + last if $data[0] =~ /^[%\\]/; # LP data + push( @pre, shift(@data) ); } - if ( @pre && !@data ) { # no LP found - @data = @pre; - @pre = (); - } - - my $kv = { %$elt }; + @pre = () if @pre && !@$data; # no LP found + my $kv = {}; $kv = parse_kv( @pre ) if @pre; - # Copy. We assume the user knows how to write LilyPond. - for ( @data ) { - print $fd $_, "\n"; - warn($_, "\n") if DEBUG; - } - - unless ( close($fd) ) { - warn("Error in Lilypond embedding: $src: $!\n"); - return; - } - - if ( $kv->{width} ) { - $pw = $kv->{width}; - } - - state $lilypond = findexe( "lilypond", "silent" ); - unless ( $lilypond ) { - warn("Error in Lilypond embedding: missing 'lilypond' tool.\n"); - return; - } - - my @cmd = ( $lilypond, qw( --png -dresolution=820) ); - push( @cmd, "--silent" ) unless DEBUG; - ( my $im1 = $img ) =~ s/\.\w+$//; - push( @cmd, "-o", $im1, $src ); - if ( sys( @cmd ) - or - ! -s $img ) { - warn("Error in Lilypond embedding\n"); - return; - } - - my $have_magick = do { - local $SIG{__WARN__} = sub {}; - local $SIG{__DIE__} = sub {}; - eval { require Image::Magick; - $Image::Magick::VERSION || "6.x?" }; - }; - if ( $have_magick ) { - warn("Using PerlMagick version ", $have_magick, "\n") - if $config->{debug}->{images} || DEBUG; - } - elsif ( is_msw() ) { - state $magick = findexe( "magick", "silent" ); - unless ( $magick ) { - warn("Error in Lilypond embedding: missing 'imagemagick/convert' tool.\n"); - return; - } - @cmd = ( $magick, "convert" ); - } - else { - state $convert = findexe( "convert", "silent" ); - unless ( $convert ) { - warn("Error in Lilypond embedding: missing 'imagemagick/convert' tool.\n"); - return; - } - @cmd = ( $convert ); - } - - my @res; - if ( $have_magick ) { - require Image::Magick; - my $image = Image::Magick->new( density => 600, background => 'white' ); - my $x = $image->Read($img); - warn $x if $x; - $x = $image->Trim; - warn $x if $x; - warn("Trim: ", join("x", $image->Get('width', 'height')). - " ", join("x", $image->Get('base-columns', 'base-rows')), - "+", join("+", $image->Get('page.x', 'page.y')), "\n") - if $config->{debug}->{images}; - - $image->Set( magick => 'jpg' ); - my $data = $image->ImageToBlob; - my $assetid = $kv->{asset} || sprintf("LYasset%03d", $imgcnt++); - warn("Created asset $assetid (jpg, ", length($data), " bytes)\n") - if $config->{debug}->{images}; - $ChordPro::Output::PDF::assets->{$assetid} = - { type => "jpg", data => $data }; - - push( @res, - { type => "image", - uri => "id=$assetid", - opts => { center => $kv->{center}, - $kv->{scale} ? ( scale => $kv->{scale} * 0.16 ) : (), - } }, - { type => "empty" }, - ) unless $kv->{asset}; - warn("Asset $assetid options:", - $kv->{scale} ? ( " scale=", $kv->{scale} * 0.16 ) : (), - " center=", $kv->{center}//0, - "\n") - if $config->{debug}->{images}; - } - else { - if ( sys( @cmd, qw(-background white -trim), $img, $img ) ) { - warn("Error in Lilypond embedding\n"); - return; - } - - warn("Reading $img...\n") if $config->{debug}->{images}; - open( my $im, '<:raw', $img ); - my $data = do { local $/; <$im> }; - close($im); - - my $assetid = $kv->{asset} || sprintf("LYasset%03d", $imgcnt); - warn("Created asset $assetid (png, ", length($data), " bytes)\n") - if $config->{debug}->{images}; - $ChordPro::Output::PDF::assets->{$assetid} = - { type => "png", data => $data }; - - push( @res,{ type => "image", - uri => "id=$assetid", - opts => { center => $kv->{center}, - $kv->{scale} ? ( scale => $kv->{scale} * 0.16 ) : (), - } }, - ) unless $kv->{asset}; - warn("Asset $assetid options:", - $kv->{scale} ? ( " scale=", $kv->{scale} * 0.16 ) : (), - " center=", $kv->{center}//0, - "\n") - if $config->{debug}->{images}; - } - - return \@res; - + $kv->{align} //= ($kv->{center}//0) ? "center" : "left"; + $kv; } 1; diff --git a/lib/ChordPro/Delegate/SVG.pm b/lib/ChordPro/Delegate/SVG.pm new file mode 100644 index 00000000..1c158b67 --- /dev/null +++ b/lib/ChordPro/Delegate/SVG.pm @@ -0,0 +1,49 @@ +#! perl + +use v5.26; +use strict; +use warnings; +use feature qw( signatures ); +no warnings "experimental::signatures"; +use utf8; + +package ChordPro::Delegate::SVG; + +use ChordPro::Utils; + +sub DEBUG() { $::config->{debug}->{svg} } + +sub svg2svg( $s, $pw, $elt ) { + + my @data = @{ $elt->{data} }; + my @pre; + + while ( $data[0] !~ /{split} //= 1; # less overhead. really. + $kv->{scale} ||= 1; + + return + { type => "image", + subtype => "svg", + line => $elt->{line}, + data => \@data, + opts => { %$kv, %{$elt->{opts}//{}} }, + }; +} + +# Pre-scan. +sub options( $data ) { + + my @pre; + + while ( $data->[0] !~ / " => ", + escape_chars => "nonascii", + print_escapes => 1, + scalar_quotes => "'", + caller_message_newline => 0, + string_max => 120, + class => { parents => 0, + linear_isa => 0, + show_methods => "none", + show_overloads => 0, + internals => 1 }; + +my $filters = [ + # Handle binary strings elegantly. + { SCALAR => sub( $ref, $ddp ) { + if ( $$ref =~ /[\000-\010\016-\037]/ ) { + my $s = $$ref; + if ( length($s) > 10 ) { + $s = substr( $s, 0, 10 ); + $s = "'$s...' (" .length($s)." bytes)"; + } + else { + $s = qq{'$s'}; + } + $s =~ s/([^[:print:]])/sprintf("\\x{%02x}", ord($1))/ge; + return $s; + } + return; + } }, + + # Try to compact hashes. + { HASH => sub( $ref, $ddp ) { + my $str = Data::Printer::Filter::HASH::parse($ref, $ddp); + ( my $s = $str ) =~ s/\s+/ /g; + my $nl = $ddp->newline; + return length($s)+length($nl) < 80 ? $s : $str; + } }, + + # Try to compact arrays. + { ARRAY => sub( $ref, $ddp ) { + my $str = do { + Data::Printer::Filter::ARRAY::parse($ref, $ddp); + }; + my $s = $str; + $s =~ s/\n\s+\[\d+\]\s+/ /g; + $s =~ s/\s+/ /g; + my $nl = $ddp->newline; + return length($s)+length($nl) < 80 ? $s : $str; + } }, + + { 'PDF::API2::Resource::XObject::Form::Hybrid' => sub ( $ref, $ddp ) { + my @bb = $ref->bbox; + return ref($ref) . " [@bb]"; + } }, + + { 'PDF::API2::Resource::XObject::Image' => sub ( $ref, $ddp ) { + return join( "", ref($ref), + " [", $ref->width, "x", $ref->height, "]", + ); + } }, +]; + +sub ddp( $ref, %options ) { + my %o = ( filters => $filters, %options ); + if ( $o{as} =~ /^(.*)\n\Z/s ) { + $o{as} = $1; + $o{caller_message_newline} = 1; + } + defined(wantarray) + ? np( $ref, %o ) + : ( -t STDERR ) + ? p( $ref, %o ) + : warn( np( $ref, %o ), "\n" ); +} + +1; diff --git a/lib/ChordPro/Output/Common.pm b/lib/ChordPro/Output/Common.pm index fd89ea5c..0dc263c3 100644 --- a/lib/ChordPro/Output/Common.pm +++ b/lib/ChordPro/Output/Common.pm @@ -10,12 +10,12 @@ package ChordPro::Output::Common; use strict; use warnings; use ChordPro::Chords; -use ChordPro::Utils qw( demarkup ); +use ChordPro::Utils qw( demarkup is_true ); use String::Interpolate::Named; use utf8; use POSIX qw(setlocale LC_TIME strftime); -use parent qw(Exporter); +use Exporter 'import'; our @EXPORT; our @EXPORT_OK; @@ -127,7 +127,7 @@ sub Roman($) { push( @EXPORT_OK, 'Roman' ); sub roman($) { - lc Roman shift; + lc( Roman(shift) ); } push( @EXPORT_OK, 'roman' ); @@ -139,6 +139,16 @@ push( @EXPORT_OK, 'roman' ); #sub PODBG() { $config->{debug}->{x1} } sub PODBG() { 0 } +# Suppress toc entry. +sub _suppresstoc { + my ( $meta ) = @_; + return !is_true($meta->{_TOC}->[0]) if exists($meta->{_TOC}); + # return unless exists($meta->{sorttitle}); + # my $st = $meta->{sorttitle}; + # defined($st) && ( $st->[0] eq "" || $st->[0] eq '""' ); + return; +} + sub prep_outlines { my ( $book, $ctl ) = @_; return [] unless $book && @$book; # unlikely @@ -149,13 +159,15 @@ sub prep_outlines { my @fields = map { /^[-+]*(.*)/ ? $1 : $_ } @{$ctl->{fields}}; if ( @fields == 1 && $fields[0] eq "songindex" ) { # Return in book order. - return [ map { [ $_->{meta}->{songindex}, $_ ] } @$book ]; + return [ map { [ $_->{meta}->{songindex}, $_ ] } + grep { !_suppresstoc($_->{meta}) } @$book ]; } return $book unless @fields; # ? my @book; foreach my $song ( @$book ) { my $meta = $song->{meta}; + next if _suppresstoc($meta); my @split; diff --git a/lib/ChordPro/Output/LaTeX.pm b/lib/ChordPro/Output/LaTeX.pm index 49367ced..beb26eaf 100644 --- a/lib/ChordPro/Output/LaTeX.pm +++ b/lib/ChordPro/Output/LaTeX.pm @@ -14,6 +14,7 @@ package ChordPro::Output::LaTeX; use strict; use warnings; +use ChordPro::Paths; use ChordPro::Output::Common; use Template; use LaTeX::Encode; @@ -52,8 +53,9 @@ sub generate_songbook { my ( $self, $sb ) = @_; my @songs; $gcfg = $::config->{latex}; - $gtemplate = Template->new({ - INCLUDE_PATH => [@{$gcfg->{template_include_path}}, ::rsc_or_file("res/templates/"), $CHORDPRO_LIBRARY], + $gtemplate = Template->new + ({ INCLUDE_PATH => [@{$gcfg->{template_include_path}}, + CP->findres("templates"), $CHORDPRO_LIBRARY], INTERPOLATE => 1, }) || die "$Template::ERROR\n"; diff --git a/lib/ChordPro/Output/PDF.pm b/lib/ChordPro/Output/PDF.pm index 72043120..3a018480 100644 --- a/lib/ChordPro/Output/PDF.pm +++ b/lib/ChordPro/Output/PDF.pm @@ -6,22 +6,25 @@ use utf8; our $config; our $options; +our $ps; + package ChordPro::Output::PDF; use strict; use warnings; use Encode qw( encode_utf8 ); -use App::Packager; use File::Temp (); use Storable qw(dclone); use List::Util qw(any); use Carp; use feature 'state'; - +use File::LoadLines qw(loadlines loadblob); use ChordPro::Output::Common qw( roman prep_outlines fmt_subst ); +use feature 'signatures'; use ChordPro::Output::PDF::Writer; +use ChordPro::Paths; use ChordPro::Utils; my $pdfapi; @@ -42,7 +45,7 @@ sub generate_songbook { return [] unless $sb->{songs}->[0]->{body}; # no songs $verbose ||= $options->{verbose}; - my $ps = $config->{pdf}; + $ps = $config->{pdf}; my $pr = (__PACKAGE__."::Writer")->new( $ps, $pdfapi ); warn("Generating PDF ", $options->{output} || "__new__.pdf", "...\n") if $options->{verbose}; @@ -93,7 +96,7 @@ sub generate_songbook { push( @book, [ $song->{meta}->{title}->[0], $song ] ); # Copy persistent assets into each of the songs. - if ( $sb->{assets} ) { + if ( $sb->{assets} && %{$sb->{assets}} ) { $song->{assets} //= {}; while ( my ($k,$v) = each %{$sb->{assets}} ) { $song->{assets}->{$k} = $v; @@ -232,14 +235,14 @@ sub generate_csv { # Create an MSPro compatible CSV for this PDF. push( @$book, [ "CSV", { meta => { tocpage => $page } } ] ); - ( my $csv = $options->{output} ) =~ s/\.pdf$/.csv/i; + my $csv = CP->sibling( $options->{output}, ext => ".csv" ); open( my $fd, '>:utf8', encode_utf8($csv) ) or die( encode_utf8($csv), ": $!\n" ); warn("Generating CSV ", encode_utf8($csv), "...\n") if $config->{debug}->{csv} || $options->{verbose}; - my $ps = $config->{pdf}; + $ps = $config->{pdf}; my $ctl = $ps->{csv}; my $sep = $ctl->{separator} // ";"; my $vsep = $ctl->{vseparator} // "|"; @@ -335,8 +338,20 @@ my $inlineannots; # format for inline annots my $chordsunder = 0; # chords under the lyrics my $chordscol = 0; # chords in a separate column my $chordscapo = 0; # capo in a separate column + my $i_tag; -our $assets; +sub pr_label_maybe { + my ( $ps, $x, $y ) = @_; + my $tag = $i_tag // ""; + $i_tag = undef; + prlabel( $ps, $tag, $x, $y ) if $tag ne ""; +} + +my $assets; +sub assets { + my ( $id ) = @_; + $assets->{$id}; +} use constant SIZE_ITEMS => [ qw( chord text chorus tab grid diagram toc title footer ) ]; @@ -345,6 +360,10 @@ sub generate_song { my ( $s, $opts ) = @_; my $pr = $opts->{pr}; + if ( $pr->{layout}->can("register_element") ) { + $pr->{layout}->register_element + ( TextLayoutImageElement->new( pdf => $pr->{pdf} ), "img" ); + } unless ( $s->{body} ) { # empty song, or embedded return unless $s->{source}->{embedding}; @@ -364,14 +383,13 @@ sub generate_song { local $config = dclone( $s->{config} // $config ); $source = $s->{source}; - $assets = $s->{assets} || {}; $suppress_empty_chordsline = $::config->{settings}->{'suppress-empty-chords'}; $suppress_empty_lyricsline = $::config->{settings}->{'suppress-empty-lyrics'}; $inlinechords = $::config->{settings}->{'inline-chords'}; $inlineannots = $::config->{settings}->{'inline-annotations'}; $chordsunder = $::config->{settings}->{'chords-under'}; - my $ps = $::config->clone->{pdf}; + $ps = $::config->clone->{pdf}; $ps->{pr} = $pr; $pr->{ps} = $ps; $ps->{_s} = $s; @@ -389,13 +407,13 @@ sub generate_song { my $dd; my $dctl; if ( $::config->{instrument}->{type} eq "keyboard" ) { - require ChordPro::Output::PDF::KeyboardDiagrams; - $dd = ChordPro::Output::PDF::KeyboardDiagrams->new($ps); + require ChordPro::Output::PDF::KeyboardDiagram; + $dd = ChordPro::Output::PDF::KeyboardDiagram->new( ps => $ps ); $dctl = $ps->{kbdiagrams}; } else { - require ChordPro::Output::PDF::StringDiagrams; - $dd = ChordPro::Output::PDF::StringDiagrams->new($ps); + require ChordPro::Output::PDF::StringDiagram; + $dd = ChordPro::Output::PDF::StringDiagram->new( ps => $ps ); $dctl = $ps->{diagrams}; } $dctl->{show} = $s->{settings}->{diagrampos} @@ -619,7 +637,7 @@ sub generate_song { if ( $bgpdf =~ /^(.+):(\d+)$/ ) { ( $bgpdf, $pg ) = ( $1, $2 ); } - $fn = ::rsc_or_file($bgpdf); + $fn = CP->findres($bgpdf); if ( -s -r $fn ) { $pg++ if $ps->{"even-odd-pages"} && !$rightpage; $pr->importpage( $fn, $pg ); @@ -631,7 +649,6 @@ sub generate_song { } $x = $ps->{__leftmargin}; - $x += $ps->{_indent}; $y = $ps->{_margintop}; $y += $ps->{headspace} if $ps->{'head-first-only'} && $class == 2; @@ -639,6 +656,7 @@ sub generate_song { $y -= imagespread( $spreadimage, $x, $y, $ps ); undef $spreadimage; } + $x += $ps->{_indent}; $ps->{_top} = $y; $col = 0; $vsp_ignorefirst = 1; @@ -806,46 +824,47 @@ sub generate_song { }; my @elts; - my $elt; # current element - my @sb = @{$sb}; - my $redo = []; - while ( @sb ) { - $elt = @$redo ? shift(@$redo) : shift(@sb); - if ( $elt->{type} eq "image" - && $elt->{opts}->{spread} ) { - if ( $spreadimage ) { - warn("Ignoring superfluous spread image\n"); + my $dbgop = sub { + my ( $elts, $pb ) = @_; + $elts //= $elts[-1]; + $elts = [ $elts ] unless ref($elts) eq 'ARRAY'; + for my $elt ( @$elts ) { + my $msg = sprintf("OP L:%2d %s (", $elt->{line}, + $pb ? "pushback($elt->{type})" : $elt->{type} ); + $msg .= " " . $elt->{subtype} if $elt->{subtype}; + $msg .= " U:" . $elt->{uri} if $elt->{uri}; + $msg .= " O:" . $elt->{orig} if $elt->{orig}; + $msg .= " D:" . $elt->{delegate} if $elt->{delegate}; + $msg .= " H:" . $elt->{handler} if $elt->{handler}; + $msg .= " )"; + $msg =~ s/\s+\(\s+\)//; + if ( $config->{debug}->{ops} > 1 ) { + require ChordPro::Dumper; + local *ChordPro::Chords::Appearance::_data_printer = sub { + my ( $self, $ddp ) = @_; + "ChordPro::Chords::Appearance('" . $self->key . "'" . + ($self->format ? (", '" . $self->format . "'") : "") . + ")"; + }; + + ChordPro::Dumper::ddp( $elt, as => $msg ); } else { - warn("Got spread image\n") if $config->{debug}->{images}; - $spreadimage //= $elt; - next; + warn( $msg, "\n" ); } } - elsif ( $elt->{type} eq "delegate" - && $elt->{subtype} eq "image" - && $elt->{data}->[0] =~ /\bspread=\d+\b$/ - ) { - if ( $spreadimage ) { - warn("Ignoring superfluous spread delegate\n"); - } - else { - my $delegate = $elt->{delegate}; - warn("Got spread delegate $delegate\n") if $config->{debug}->{images}; - my $pkg = __PACKAGE__; - $pkg =~ s/::Output::\w+$/::Delegate::$delegate/; - eval "require $pkg" || die($@); - my $hd = $pkg->can($elt->{handler}) // - die("PDF: Missing delegate handler ${pkg}::$elt->{handler}\n"); - - my $pw = $ps->{papersize}->[0] - - $ps->{marginleft} - - $ps->{marginright}; - $redo = $hd->( $s, $pw, $elt ); - next; - } - } - push( @elts, $elt ); + }; + + #### CODE STARTS HERE #### + +# prepare_assets( $s, $pr ); + + if ( $s->{spreadimage} ) { + $spreadimage = $assets->{$s->{spreadimage}->{id}}; +# { type => "image", +# id => $s->{spreadimage}->{id}, +# opts => { spread => $s->{spreadimage}->{space} } +# }; } # Get going. @@ -862,9 +881,15 @@ sub generate_song { my $did = 0; my $curctx = ""; + my $elt; # current element + @elts = @$sb; # song elements while ( @elts ) { $elt = shift(@elts); + if ( $config->{debug}->{ops} ) { + $dbgop->($elt); + } + if ( $elt->{type} eq "newpage" ) { $newpage->(); next; @@ -878,6 +903,8 @@ sub generate_song { if ( $elt->{type} ne "set" && !$did++ ) { # Insert top/left/right/bottom chord diagrams. $chorddiagrams->() unless $dctl->{show} eq "below"; + # Prepare the assets now we know the page width. + prepare_assets( $s, $pr ); showlayout($ps) if $ps->{showlayout} || $config->{debug}->{spacing}; } @@ -885,7 +912,12 @@ sub generate_song { my $y0 = $y; warn("***SHOULD NOT HAPPEN1***") if $s->{structure} eq "structured"; - $vsp_ignorefirst = 0, next if $vsp_ignorefirst; + if ( $vsp_ignorefirst ) { + if ( @elts && $elts[0]->{type} !~ /empty|ignore/ ) { + $vsp_ignorefirst = 0; + } + next; + } $pr->show_vpos( $y, 0 ) if $config->{debug}->{spacing}; my $vsp = empty_vsp( $elt, $ps ); $y -= $vsp; @@ -1071,11 +1103,13 @@ sub generate_song { ", C=$cells, GBW=$grid_barwidth, W=", $grid_cellwidth, "\n") if $config->{debug}->{spacing}; - gridline( $elt, $x, $y, - $grid_cellwidth, - $grid_barwidth, - $grid_margin, - $ps, song => $s ); + require ChordPro::Output::PDF::Grid; + ChordPro::Output::PDF::Grid::gridline + ( $elt, $x, $y, + $grid_cellwidth, + $grid_barwidth, + $grid_margin, + $ps, song => $s ); $y -= $vsp; $pr->show_vpos( $y, 1 ) if $config->{debug}->{spacing}; @@ -1109,31 +1143,6 @@ sub generate_song { next; } - if ( $elt->{type} eq "delegate" ) { - if ( $elt->{subtype} =~ /^image(?:-(\w+))?$/ ) { - my $delegate = $1 // $elt->{delegate}; - my $pkg = __PACKAGE__; - $pkg =~ s/::Output::\w+$/::Delegate::$delegate/; - eval "require $pkg" || die($@); - my $hd = $pkg->can($elt->{handler}) // - die("PDF: Missing delegate handler ${pkg}::$elt->{handler}\n"); - my $pw; # available width - if ( $ps->{columns} > 1 ) { - $pw = $ps->{columnoffsets}->[1] - - $ps->{columnoffsets}->[0] - - $ps->{columnspace}; - } - else { - $pw = $ps->{__rightmargin} - $ps->{_leftmargin}; - } - my $res = $hd->( $s, $pw, $elt ); - next unless $res; # assume errors have been given - unshift( @elts, @$res ); - next; - } - die("PDF: Unsupported delegation $elt->{subtype}\n"); - } - if ( $elt->{type} eq "image" ) { # Images are slightly more complex. # Only after establishing the desired height we can issue @@ -1160,113 +1169,13 @@ sub generate_song { $y -= $vsp; $pr->show_vpos( $y, 1 ) if $config->{debug}->{spacing}; - next; - } - - if ( $elt->{type} eq "svg" ) { - # We turn SVG into one (or more) XForm objects. - - require SVGPDF; - SVGPDF->VERSION(0.070); - - # Note we need special font and text handlers. - my $p = SVGPDF->new - ( pdf => $ps->{pr}->{pdf}, - fc => sub { svg_fonthandler( $ps, @_ ) }, - tc => sub { svg_texthandler( $ps, @_ ) }, - atts => { debug => $config->{debug}->{svg} > 1, - verbose => $config->{debug}->{svg}, - } ); - my $o = $p->process( $elt->{uri} ); - warn("PDF: SVG objects: ", 0+@$o, "\n") - if $config->{debug}->{svg} || !@$o; - if ( ! @$o ) { - warn("Error in SVG embedding (no SVG objects found)\n"); - next; - } - - my @res; - my $i = 0; - for my $xo ( @$o ) { - state $imgcnt = 0; - $i++; - my $assetid = sprintf("XFOasset%03d", $imgcnt++); - $assets->{$assetid} = { type => "xform", data => $xo }; - my $sep = $i == @$o ? 0 : $elt->{opts}->{sep} || 0; - - push( @res, - { type => "xform", - width => $xo->{width}, - height => $xo->{height}, - vwidth => $xo->{vwidth}, - vheight => $xo->{vheight}, - id => $assetid, - opts => { center => $elt->{opts}->{center}, - scale => $elt->{opts}->{scale} || 1, - sep => $sep }, - } - ); - warn("Created asset $assetid (xform, ", - $xo->{vwidth}, "x", $xo->{vheight}, ")", - " scale=", $elt->{opts}->{scale} || 1, - " center=", $elt->{opts}->{center}//0, - " sep=", $sep, - "\n") - if $config->{debug}->{images}; - } - unshift( @elts, @res ); - next; - } - - if ( $elt->{type} eq "xform" ) { - my $h = $elt->{height};# + ($elt->{opts}->{sep}||0); - my $w = $elt->{width}; - my $vh = $elt->{vheight}; - my $vw = $elt->{vwidth}; - my $xo = $assets->{ $elt->{id} }; - - my $scale = min( $vw / $w, $vh / $h ); - my $sep = $elt->{opts}->{sep} || 0; - - # Available width and height. - my $pw; - if ( $ps->{columns} > 1 ) { - $pw = $ps->{columnoffsets}->[1] - - $ps->{columnoffsets}->[0] - - $ps->{columnspace}; - } - else { - $pw = $ps->{__rightmargin} - $ps->{_leftmargin}; - } - my $ph = $ps->{_margintop} - $ps->{_marginbottom}; - - if ( $w * $scale > $pw ) { - $scale = $pw / $w; - } - if ( $h * $scale > $ph ) { - $scale = $ph / $h; + if ( $elt->{multi} && !$elt->{msel} ) { + my $i = @{ $elt->{multi} } - 1; + while ( $i > 0 ) { + unshift( @elts, { %$elt, msel => $i } ); + $i--; + } } - warn("XForm asset ", $elt->{id}, " (", - $vw, "x", $vh, ")", - " [$x,$y]", - " scale=", $scale, - " center=", $elt->{opts}->{center}//0, - " sep=", $sep, - "\n") - if $config->{debug}->{images}; - - $scale *= $elt->{opts}->{scale}; - my $vsp = $h * $scale; - $checkspace->($vsp); - $ps->{pr}->show_vpos( $y, 1 ) if $config->{debug}->{spacing}; - - $pr->{pdfgfx}->object( $xo->{data}->{xo}, - $x-$xo->{data}->{vbox}->[0]*$scale, - $y, $scale ); - - $y -= $vsp + $sep; - $pr->show_vpos( $y, 1 ) if $config->{debug}->{spacing}; - next; } @@ -1378,8 +1287,10 @@ sub generate_song { } else { # Restore default. + my $sz = $ps->{fonts}->{$1}->{size}; $ps->{fonts}->{$f} = { %{ $pr->{_df}->{$f} } }; +# $ps->{fonts}->{$1}->{size} = $sz; } $pr->init_font($f); } @@ -1563,10 +1474,10 @@ sub font_ul { } sub prlabel { - my ( $ps, $label, $x, $y, $font) = @_; + my ( $ps, $label, $x, $y ) = @_; return if $label eq "" || $ps->{_indent} == 0; my $align = $ps->{labels}->{align}; - $font ||= $ps->{fonts}->{label} || $ps->{fonts}->{text}; + my $font= $ps->{fonts}->{label} || $ps->{fonts}->{text}; $font->{size} ||= $font->{fd}->{size}; $ps->{pr}->setfont($font); # for strwidth. for ( split( /\\n/, $label ) ) { @@ -1627,7 +1538,9 @@ sub defrag { } elsif ( $a =~ m;^<\s*(\w+)(.*)>$; ) { my $k = $1; - push( @stack, "<$k$2>" ); + my $v = $2; + # Do not push if self-closed. + push( @stack, "<$k$v>" ) unless $v =~ m;/\s*$;; } push( @r, $a ); } @@ -1690,9 +1603,6 @@ sub songline { my $ftext; my $ytext; - my $tag = $i_tag // ""; - $i_tag = undef; - my @phrases = @{ defrag( $elt->{phrases} ) }; if ( $type =~ /^comment/ ) { @@ -1701,7 +1611,7 @@ sub songline { my $song = $opts{song}; $x += $opts{indent} if $opts{indent}; $x += $elt->{indent} if $elt->{indent}; - prlabel( $ps, $tag, $x, $ytext ); + pr_label_maybe( $ps, $x, $ytext ); my $t = $elt->{text}; if ( $elt->{chords} ) { $t = ""; @@ -1717,15 +1627,18 @@ sub songline { } my ( $text, $ex ) = wrapsimple( $pr, $t, $x, $ftext ); $pr->text( $text, $x, $ytext, $ftext ); + my $wi = $pr->strwidth( $config->{settings}->{wrapindent}//"x" ); return $ex ne "" - ? { %$elt, indent => $pr->strwidth("x"), text => $ex, chords => undef } + ? { %$elt, + indent => $wi, + text => $ex, chords => undef } : undef; } if ( $type eq "tabline" ) { $ftext = $fonts->{tab}; $ytext = $ytop - font_bl($ftext); $x += $opts{indent} if $opts{indent}; - prlabel( $ps, $tag, $x, $ytext ); + pr_label_maybe( $ps, $x, $ytext ); $pr->text( $elt->{text}, $x, $ytext, $ftext, undef, "no markup" ); return; } @@ -1745,11 +1658,16 @@ sub songline { my $x = $x; $x += $opts{indent} if $opts{indent}; $x += $elt->{indent} if $elt->{indent}; - prlabel( $ps, $tag, $x, $ytext ); + pr_label_maybe( $ps, $x, $ytext ); my ( $text, $ex ) = wrapsimple( $pr, join( "", @phrases ), $x, $ftext ); $pr->text( $text, $x, $ytext, $ftext ); - return $ex ne "" ? { %$elt, indent => $pr->strwidth("x"), phrases => [$ex] } : undef; + my $wi = $pr->strwidth( $config->{settings}->{wrapindent}//"x" ); + return $ex ne "" + ? { %$elt, + indent => $wi, + phrases => [$ex] } + : undef; } if ( $chordscol || $inlinechords ) { @@ -1816,8 +1734,7 @@ sub songline { 0.25, $ps->{theme}->{foreground} ); # Print the text. - prlabel( $ps, $tag, $x, $ytext ); - $tag = ""; + pr_label_maybe( $ps, $x, $ytext ); $x = $pr->text( $phrase, $x, $ytext, $ftext ); # Collect chords to be printed in the side column. @@ -1842,8 +1759,7 @@ sub songline { } # Do not indent chorus labels (issue #81). - prlabel( $ps, $tag, $x-$opts{indent}, $ytext ); - $tag = ""; + pr_label_maybe( $ps, $x-$opts{indent}, $ytext ); if ( $inlinechords ) { $x = $pr->text( $phrase, $xt0, $ytext, $ftext ); } @@ -1910,376 +1826,37 @@ sub songline { return; } -sub is_bar { - exists( $_[0]->{class} ) && $_[0]->{class} eq "bar"; -} - -sub gridline { - my ( $elt, $x, $y, $cellwidth, $barwidth, $margin, $ps, %opts ) = @_; - - # Grid context. - - my $pr = $ps->{pr}; - my $fonts = $ps->{fonts}; - - my $tag = $i_tag // ""; - $i_tag = undef; - - # Use the chords font for the chords, and for the symbols size. - my $fchord = { %{ $fonts->{grid} || $fonts->{chord} } }; - delete($fchord->{background}); - $y -= font_bl($fchord); - - prlabel( $ps, $tag, $x, $y ); - - $x += $barwidth; - $cellwidth += $barwidth; - - $elt->{tokens} //= [ {} ]; - - my $firstbar; - my $lastbar; - foreach my $i ( 0 .. $#{ $elt->{tokens} } ) { - next unless is_bar( $elt->{tokens}->[$i] ); - $lastbar = $i; - $firstbar //= $i; - } - - my $prevbar = -1; - my @tokens = @{ $elt->{tokens} }; - my $t; - - if ( $margin->[0] ) { - $x -= $barwidth; - if ( $elt->{margin} ) { - my $t = $elt->{margin}; - if ( $t->{chords} ) { - $t->{text} = ""; - for ( 0..$#{ $t->{chords} } ) { - $t->{text} .= $t->{chords}->[$_]->chord_display . $t->{phrases}->[$_]; - } - } - $pr->text( $t->{text}, $x, $y, $fonts->{comment} ); - } - $x += $margin->[0] * $cellwidth + $barwidth; - } - - my $ctl = $pr->{ps}->{grids}->{cellbar}; - my $col = $pr->{ps}->{grids}->{symbols}->{color}; - my $needcell = $ctl->{width}; - - state $prevvoltastart; - my $align; - if ( $prevvoltastart && @tokens - && $tokens[0]->{class} eq "bar" && $tokens[0]->{align} ) { - $align = $prevvoltastart; - } - $prevvoltastart = 0; - - my $voltastart; - foreach my $i ( 0 .. $#tokens ) { - my $token = $tokens[$i]; - my $sz = $fchord->{size}; - - if ( $token->{class} eq "bar" ) { - $x -= $barwidth; - if ( $voltastart ) { - pr_voltafinish( $voltastart, $y, $x - $voltastart, $sz, $col, $pr ); - $voltastart = 0; - } - - $t = $token->{symbol}; - if ( 0 ) { - $t = "{" if $t eq "|:"; - $t = "}" if $t eq ":|"; - $t = "}{" if $t eq ":|:"; - } - else { - $t = "|:" if $t eq "{"; - $t = ":|" if $t eq "}"; - $t = ":|:" if $t eq "}{"; - } - - my $lcr = -1; # left, center, right - $lcr = 0 if $i > $firstbar; - $lcr = 1 if $i == $lastbar; - - if ( $t eq "|" ) { - if ( $token->{volta} ) { - if ( $align ) { - $x = $align; - $lcr = 0; - } - $voltastart = - pr_rptvolta( $x, $y, $lcr, $sz, $col, $pr, $token ); - $prevvoltastart ||= $x; - } - else { - pr_barline( $x, $y, $lcr, $sz, $col, $pr ); - } - } - elsif ( $t eq "||" ) { - pr_dbarline( $x, $y, $lcr, $sz, $col, $pr ); - } - elsif ( $t eq "|:" ) { - pr_rptstart( $x, $y, $lcr, $sz, $col, $pr ); - } - elsif ( $t eq ":|" ) { - pr_rptend( $x, $y, $lcr, $sz, $col, $pr ); - } - elsif ( $t eq ":|:" ) { - pr_rptendstart( $x, $y, $lcr, $sz, $col, $pr ); - } - elsif ( $t eq "|." ) { - pr_endline( $x, $y, $lcr, $sz, $col, $pr ); - } - elsif ( $t eq " %" ) { # repeat2Bars - pr_repeat( $x+$sz/2, $y, 0, $sz, $col, $pr ); - } - else { - die($t); # can't happen - } - $x += $barwidth; - $prevbar = $i; - $needcell = 0; - next; - } - - if ( $token->{class} eq "repeat2" ) { - # For repeat2Bars, change the next bar line to pseudo-bar. - my $k = $prevbar + 1; - while ( $k <= $#tokens - && !is_bar($tokens[$k]) ) { - $k++; - } - $tokens[$k] = { symbol => " %", class => "bar" }; - $x += $cellwidth; - $needcell = 0; - next; - } - - pr_cellline( $x-$barwidth, $y, 0, $sz, $ctl->{width}, - $pr->_fgcolor($ctl->{color}), $pr ) - if $needcell; - $needcell = $ctl->{width}; - - if ( $token->{class} eq "chord" || $token->{class} eq "chords" ) { - my $tok = $token->{chords} // [ $token->{chord} ]; - my $cellwidth = $cellwidth / @$tok; - for my $t ( @$tok ) { - $x += $cellwidth, next if $t eq ''; - $t = $t->chord_display; - $pr->text( $t, $x, $y, $fchord ); - $x += $cellwidth; - } - } - elsif ( exists $token->{chord} ) { - # I'm not sure why not testing for class = chord... - warn("Chord token without class\n") - unless $token->{class} eq "chord"; - my $t = $token->{chord}; - $t = $t->chord_display; - $pr->text( $t, $x, $y, $fchord ) - unless $token eq "."; - $x += $cellwidth; - } - elsif ( $token->{class} eq "slash" ) { - $pr->text( "/", $x, $y, $fchord ); - $x += $cellwidth; - } - elsif ( $token->{class} eq "space" ) { - $x += $cellwidth; - } - elsif ( $token->{class} eq "repeat1" ) { - $t = $token->{symbol}; - my $k = $prevbar + 1; - while ( $k <= $#tokens - && !is_bar($tokens[$k]) ) { - $k++; - } - pr_repeat( $x + ($k - $prevbar - 1)*$cellwidth/2, $y, - 0, $fchord->{size}, $col, $pr ); - $x += $cellwidth; - } - if ( $x > $ps->{papersize}->[0] ) { - # This should be signalled by the parser. - # warn("PDF: Too few cells for content\n"); - last; - } - } - - if ( $margin->[1] && $elt->{comment} ) { - my $t = $elt->{comment}; - if ( $t->{chords} ) { - $t->{text} = ""; - for ( 0..$#{ $t->{chords} } ) { - $t->{text} .= $t->{chords}->[$_] . $t->{phrases}->[$_]; - } - } - $pr->text( " " . $t->{text}, $x, $y, $fonts->{comment} ); - } -} - -sub pr_cellline { - my ( $x, $y, $lcr, $sz, $w, $col, $pr ) = @_; - $x -= $w / 2 * ($lcr + 1); - $pr->vline( $x, $y+0.9*$sz, $sz, $w, $col ); -} - -sub pr_barline { - my ( $x, $y, $lcr, $sz, $col, $pr ) = @_; - my $w = $sz / 10; # glyph width = $w - $x -= $w / 2 * ($lcr + 1); - $pr->vline( $x, $y+0.9*$sz, $sz, $w, $col ); -} - -sub pr_dbarline { - my ( $x, $y, $lcr, $sz, $col, $pr ) = @_; - my $w = $sz / 10; # glyph width = 3 * $w - $x -= 1.5 * $w * ($lcr + 1); - $pr->vline( $x, $y+0.9*$sz, $sz, $w, $col ); - $x += 2 * $w; - $pr->vline( $x, $y+0.9*$sz, $sz, $w, $col ); -} - -sub pr_rptstart { - my ( $x, $y, $lcr, $sz, $col, $pr ) = @_; - my $w = $sz / 10; # glyph width = 3 * $w - $x -= 1.5 * $w * ($lcr + 1); - $pr->vline( $x, $y+0.9*$sz, $sz, $w, $col ); - $x += 2 * $w; - $y += 0.55 * $sz; - $pr->line( $x, $y, $x, $y+$w, $w, $col ); - $y -= 0.4 * $sz; - $pr->line( $x, $y, $x, $y+$w, $w, $col ); -} - -sub pr_rptvolta { - my ( $x, $y, $lcr, $sz, $symcol, $pr, $token ) = @_; - my $w = $sz / 10; # glyph width = 3 * $w - my $col = $pr->{ps}->{grids}->{volta}->{color}; - my $ret = $x -= 1.5 * $w * ($lcr + 1); - $pr->vline( $x, $y+0.9*$sz, $sz, $w, $col ); - $x += 2 * $w; - my $font = $pr->{ps}->{fonts}->{grid}; - $pr->setfont($font); - $pr->text( "" . $token->{volta} . "", - $x-$w/2, $y, $font ); - $ret; -} - -sub pr_voltafinish { - my ( $x, $y, $width, $sz, $symcol, $pr ) = @_; - my $w = $sz / 10; # glyph width = 3 * $w - my ( $col, $span ) = @{$pr->{ps}->{grids}->{volta}}{qw(color span)}; - $pr->hline( $x, $y+0.9*$sz+$w/4, $width*$span, $w/2, $col ); -} - -sub pr_rptend { - my ( $x, $y, $lcr, $sz, $col, $pr ) = @_; - my $w = $sz / 10; # glyph width = 3 * $w - $x -= 1.5 * $w * ($lcr + 1); - $pr->vline( $x + 2*$w, $y+0.9*$sz, $sz, $w, $col ); - $y += 0.55 * $sz; - $pr->line( $x, $y, $x, $y+$w, $w, $col ); - $y -= 0.4 * $sz; - $pr->line( $x, $y, $x, $y+$w, $w, $col ); -} - -sub pr_rptendstart { - my ( $x, $y, $lcr, $sz, $col, $pr ) = @_; - my $w = $sz / 10; # glyph width = 5 * $w - $x -= 2.5 * $w * ($lcr + 1); - $pr->vline( $x + 2*$w, $y+0.9*$sz, $sz, $col, , $w ); - $y += 0.55 * $sz; - $pr->line( $x, $y, $x , $y+$w, $col, , $w ); - $pr->line( $x+4*$w, $y, $x+4*$w, $y+$w, $col, , $w ); - $y -= 0.4 * $sz; - $pr->line( $x, $y, $x, $y+$w, $col, , $w ); - $pr->line( $x+4*$w, $y, $x+4*$w, $y+$w, $col, , $w ); -} - -sub pr_repeat { - my ( $x, $y, $lcr, $sz, $col, $pr ) = @_; - my $w = $sz / 3; # glyph width = 3 * $w - $x -= 1.5 * $w * ($lcr + 1); - my $lw = $sz / 10; - $x -= $w / 2; - $pr->line( $x, $y+0.2*$sz, $x + $w, $y+0.7*$sz, $lw ); - $pr->line( $x, $y+0.6*$sz, $x + 0.07*$sz , $y+0.7*$sz, $lw ); - $x += $w; - $pr->line( $x - 0.05*$sz, $y+0.2*$sz, $x + 0.02*$sz, $y+0.3*$sz, $lw ); -} - -sub pr_endline { - my ( $x, $y, $lcr, $sz, $col, $pr ) = @_; - my $w = $sz / 10; # glyph width = 2 * $w - $x -= 0.75 * $w * ($lcr + 1); - $pr->vline( $x, $y+0.85*$sz, 0.9*$sz, 2*$w ); -} - sub imageline_vsp { } sub imageline { my ( $elt, $x, $ps, $gety ) = @_; - my $opts = $elt->{opts}; + my $x0 = $x; my $pr = $ps->{pr}; - - my $img; - if ( $elt->{uri} =~ /^id=(.+)/ ) { - my $id = $1; - unless ( exists( $assets->{$id} ) ) { - unless ( exists( $config->{assets}->{$id} ) ) { - return "Unknown asset: id=$id"; - } - my $a = $config->{assets}->{$id}; - if ( $a->{src} && !$a->{data} ) { - use Image::Info; - open( my $fd, '<:raw', $a->{src} ); - unless ( $fd ) { - return $a->{src} . ": $!"; - } - my $data = do { local $/; <$fd> }; - # Get info. - my $info = Image::Info::image_info(\$data); - if ( $info->{error} ) { - return $info->{error}; - } - - # Store in assets. - $assets //= {}; - $assets->{$id} = - { data => $data, type => $info->{file_ext}, - width => $info->{width}, height => $info->{height}, - }; - - if ( $config->{debug}->{images} ) { - warn("asset[$id] ", length($data), " bytes, ", - "width=$info->{width}, height=$info->{height}", - "\n"); - } - } - } - unless ( $assets->{$id}->{data} ) { - return "Unhandled asset: id=$id"; - } - } - elsif ( $elt->{uri} =~ /^chord:(.*)/) { - $img = placeholder->new( $1, - $ps->{dd}->hsp0( undef, $ps ), - $ps->{dd}->vsp0( undef, $ps ) ); + my $id = $elt->{id}; + my $opts = { %{$assets->{$id}->{opts}//{}}, %{$elt->{opts}//{}} }; + my $img = $assets->{$id}->{data}; + my $label = $opts->{label}; + my $width = $opts->{width}; + my $height = $opts->{height}; + my $avwidth = $assets->{$id}->{vwidth}; + my $avheight = $assets->{$id}->{vheight}; + unless ( $img ) { + return "Unhandled image type: asset=$id"; } - elsif ( ! -s $elt->{uri} ) { - return "$!: " . $elt->{uri}; + if ( $assets->{$id}->{multi} ) { + $elt->{multi} = $assets->{$id}->{multi}; } - - warn("get_image ", $elt->{uri}, "\n") if $config->{debug}->{images}; - $img //= eval { $pr->get_image($elt) }; - unless ( $img ) { - warn($@); - return "Unhandled image type: " . $elt->{uri}; + if ( $elt->{msel} ) { + for ( $assets->{$id}->{multi}->[$elt->{msel}] ) { + $img = $_->{xo}; + # Take vwidth/vheight from subimage. + $avwidth = $_->{vwidth}; + $avheight = $_->{vheight}; + } + $width = $height = 0; + $label = ""; } # Available width and height. @@ -2292,18 +1869,28 @@ sub imageline { else { $pw = $ps->{__rightmargin} - $ps->{_leftmargin}; } + $pw -= $ps->{_indent}; my $ph = $ps->{_margintop} - $ps->{_marginbottom}; - if ( $opts->{width} && $opts->{width} =~ /^(\d+(?:\.\d+)?)\%$/ ) { - $opts->{width} = $1/100 * $pw; + if ( $width && $width =~ /^(\d+(?:\.\d+)?)\%$/ ) { + $width = $1/100 * $pw; } - if ( $opts->{height} && $opts->{height} =~ /^(\d+(?:\.\d+)?)\%$/ ) { - $opts->{height} = $1/100 * $ph; + if ( $height && $height =~ /^(\d+(?:\.\d+)?)\%$/ ) { + $height = $1/100 * $ph; } my $scale = 1; - my ( $w, $h ) = ( $opts->{width} || $img->width, - $opts->{height} || $img->height ); + my ( $w, $h ) = ( $width || $avwidth || $img->width, + $height || $avheight || $img->height ); + + # Scale proportionally if width xor height was explicitly requested. + if ( $width && !$height ) { + $h = $width / ($avwidth || $img->width) * ($avheight || $img->height); + } + elsif ( !$width && $height ) { + $w = $height / ($avheight || $img->height) * ($avwidth || $img->width); + } + if ( $config->{debug}->{x1} ) { @@ -2330,7 +1917,7 @@ sub imageline { $scale = $ph / $h; } if ( $opts->{scale} ) { - $scale *= $opts->{scale}; + $scale *= $opts->{scale} =~ /^(\d+(?:\.\d+)?)\%$/ ? $1/100 : $opts->{scale}; } } @@ -2338,23 +1925,34 @@ sub imageline { warn("Image scale: $scale\n") if $config->{debug}->{images}; $h *= $scale; $w *= $scale; - if ( $opts->{center} ) { - $x += ($pw - $w) / 2; - warn("Image center: $_[1] -> $x\n") if $config->{debug}->{images}; + + my $anchor = $opts->{anchor} //= "float"; + my $ox = $opts->{x}; + my $oy = $opts->{y}; + + my $align; + if ( $anchor eq "float" ) { + $align = $opts->{align}; + $align //= ( $opts->{center} // 1 ) ? "center" : "left"; + # Note that image is placed aligned on $x. + if ( $align eq "center" ) { + $x += ($pw - $ps->{_indent}) / 2; + } + elsif ( $align eq "right" ) { + $x += $pw - $ps->{_indent}; + } + warn("Image $align: $_[1] -> $x\n") if $config->{debug}->{images}; } + $align //= "left"; my $y = $gety->($h); # may have been changed by checkspace - if ( defined ( my $tag = $i_tag // $opts->{label} ) ) { - $i_tag = undef; + if ( defined ( my $tag = $i_tag // $label ) ) { + $i_tag = $tag; my $ftext = $ps->{fonts}->{comment}; my $ytext = $y - font_bl($ftext); - prlabel( $ps, $tag, $x, $ytext ); + pr_label_maybe( $ps, $x0, $ytext ); } - my $anchor = $opts->{anchor} //= "float"; - my $ox = $opts->{x}; - my $oy = $opts->{y}; - my $calc = sub { my ( $l, $r, $t, $b, $mirror ) = @_; my $_ox = $ox // 0; @@ -2395,26 +1993,20 @@ sub imageline { $x += $ox if defined $ox; $y -= $oy if defined $oy; - if ( ref($img) eq "placeholder" ) { - warn( sprintf("add_chord %s %.1f %.1f %.1f %.1f (%s x%+.1f y%+.1f)\n", - $img->name, $x, $y, $w, $h, - $anchor, - $ox//0, $oy//0 - )) if $config->{debug}->{images}; - local $ps->{diagrams}->{width} = $ps->{diagrams}->{width} * $scale; - local $ps->{diagrams}->{height} = $ps->{diagrams}->{height} * $scale; - $ps->{dd}->draw( $ps->{_s}->{chordsinfo}->{$img->name}, $x, $y, $ps ); - } - else { - warn( sprintf("add_image %.1f %.1f %.1f %.1f (%s x%+.1f y%+.1f)\n", - $x, $y, $w, $h, - $anchor, - $ox//0, $oy//0 - )) if $config->{debug}->{images}; - - $pr->add_image( $img, $x, $y, $w, $h, $opts->{border} || 0 ); - } - warn("done\n") if $config->{debug}->{images}; + warn( sprintf("add_image x=%.1f y=%.1f w=%.1f h=%.1f (%s x%+.1f y%+.1f) %s\n", + $x, $y, $w, $h, + $anchor, + $ox//0, $oy//0, $align, + )) if $config->{debug}->{images}; + +# $pr->add_image( $img, $x, $y, $w, $h, $opts->{border} || 0 ); + $pr->add_object( $img, $x-2, $y, + xscale => $w/$img->width, + yscale => $h/$img->height, + border => $opts->{border} || 0, + valign => "top", + align => $align, + ); if ( $anchor eq "float" ) { return $h + ($oy//0); @@ -2424,24 +2016,16 @@ sub imageline { sub imagespread { my ( $elt, $x, $y, $ps ) = @_; - + ::dump($elt); my $opts = $elt->{opts}; my $pr = $ps->{pr}; - if ( $elt->{uri} =~ /^id=(.+)/ ) { - return "Unknown asset: id=$1" - unless exists( $assets->{$1} ); - } - elsif ( ! -s $elt->{uri} ) { - return "$!: " . $elt->{uri}; - } - - warn("get_image ", $elt->{uri}, "\n") if $config->{debug}->{images}; - my $img = eval { $pr->get_image($elt) }; - unless ( $img ) { - warn($@); - return "Unhandled image type: " . $elt->{uri}; - } + my $tag = "id=" . $elt->{opts}->{id}; + return "Unknown asset: $tag" + unless exists( $assets->{$elt->{opts}->{id}} ); + my $img = $assets->{$elt->{opts}->{id}}->{data}; + return "Unhandled asset: $tag" + unless $img; # Available width and height. my $pw = $ps->{__rightmargin} - $ps->{_leftmargin}; @@ -2450,6 +2034,10 @@ sub imagespread { my $scale = 1; my ( $w, $h ) = ( $opts->{width} || $img->width, $opts->{height} || $img->height ); + + if ( $config->{debug}->{x1} ) { + + # Current approach: user scale overrides. if ( defined $opts->{scale} ) { $scale = $opts->{scale} || 1; } @@ -2461,24 +2049,46 @@ sub imagespread { $scale = $ph / $h; } } + } + else { + + # Better, but may break things. + if ( $w > $pw ) { + $scale = $pw / $w; + } + if ( $h*$scale > $ph ) { + $scale = $ph / $h; + } + if ( $opts->{scale} ) { + $scale *= $opts->{scale}; + } + + } + warn("Image scale: $scale\n") if $config->{debug}->{images}; $h *= $scale; $w *= $scale; - if ( $opts->{center} ) { - $x += ($pw - $w) / 2; - warn("Image center: $_[1] -> $x\n") if $config->{debug}->{images}; - } - if ( defined ( my $tag = $i_tag // $opts->{label} ) ) { - $i_tag = undef; - my $ftext = $ps->{fonts}->{comment}; - my $ytext = $y - font_bl($ftext); - prlabel( $ps, $tag, $x, $ytext ); + my $align = $opts->{align}; + $align //= ( $opts->{center} // 1 ) ? "center" : "left"; + # Note that image is placed aligned on $x. + if ( $align eq "center" ) { + $x += $pw / 2; } + elsif ( $align eq "right" ) { + $x += $pw; + } + warn("Image $align: $_[1] -> $x\n") if $config->{debug}->{images}; warn("add_image\n") if $config->{debug}->{images}; - $pr->add_image( $img, $x, $y, $w, $h, $opts->{border} || 0 ); - warn("done\n") if $config->{debug}->{images}; + # $pr->add_image( $img, $x, $y, $w, $h, $opts->{border} || 0 ); + $pr->add_object( $img, $x, $y, + xscale => $w/$img->width, + yscale => $h/$img->height, + border => $opts->{border} || 0, + valign => "top", + align => $align, + ); return $h + $elt->{opts}->{spread}; # vertical size } @@ -2953,6 +2563,8 @@ sub wrap { my @rchords; my @rphrases; my $m = $pr->{ps}->{__rightmargin}; + my $wi = $pr->strwidth( $config->{settings}->{wrapindent}//"x", + $pr->{ps}->{fonts}->{text} ); #warn("WRAP x=$x rm=$m w=", $m - $x, "\n"); while ( @chords ) { @@ -3009,14 +2621,14 @@ sub wrap { unshift( @phrases, $ex ); push( @$res, { %$elt, chords => [@rchords], phrases => [@rphrases] } ); - $x = $_[2] + $pr->strwidth("x"); - $res->[-1]->{indent} = $pr->strwidth("x") if @$res > 1; + $x = $_[2] + $wi;; + $res->[-1]->{indent} = $wi if @$res > 1; @rchords = (); @rphrases = (); } } push( @$res, { %$elt, chords => \@rchords, phrases => \@rphrases } ); - $res->[-1]->{indent} = $pr->strwidth("x") if @$res > 1; + $res->[-1]->{indent} = $wi if @$res > 1; return $res; } @@ -3029,6 +2641,222 @@ sub wrapsimple { $pr->wrap( $text, $pr->{ps}->{__rightmargin} - $x ); } +sub prepare_assets { + my ( $s, $pr ) = @_; + + my %sa = %{$s->{assets}//{}}; # song assets + + # All elements generate zero or one display items, except for SVG images + # than can result in a series of display items. + # So we first scan the list for SVG and delegate items and turn these + # into simple display items. + + my $pw = $ps->{__rightmargin} - $ps->{__leftmargin} + - $ps->{_indent}; + my $cw = ( $pw - ( $ps->{columns} - 1 ) * $ps->{columnspace} ) /$ps->{columns}; + warn("PDF: Preparing ", scalar(keys %sa), " image", + keys(%sa) == 1 ? "" : "s", ", pw=$pw, cw=$cw\n") + if $config->{debug}->{images} || $config->{debug}->{assets}; + for my $id ( sort keys %sa ) { + my $elt = $sa{$id}; + + $elt->{subtype} //= "image" if $elt->{uri}; + + if ( $elt->{type} eq "image" && $elt->{subtype} eq "delegate" ) { + my $delegate = $elt->{delegate}; + warn("PDF: Preparing delegate $delegate, handler ", + $elt->{handler}, "\n") if $config->{debug}->{images}; + + my $pkg = __PACKAGE__; + $pkg =~ s/::Output::[:\w]+$/::Delegate::$delegate/; + eval "require $pkg" || die($@); + my $hd = $pkg->can($elt->{handler}) // + die("PDF: Missing delegate handler ${pkg}::$elt->{handler}\n"); + unless ( $elt->{data} ) { + $elt->{data} = [ loadlines($elt->{uri}) ]; + } + + # Determine actual width. + my $w = defined($elt->{opts}->{spread}) ? $pw : $cw; +# $w -= $ps->{_indent}; + $w = $elt->{opts}->{width} + if $elt->{opts}->{width} && $elt->{opts}->{width} < $w; + + my $res = $hd->( $s, $w, $elt ); + if ( $res ) { + warn( "PDF: Preparing delegate $delegate, handler ", + $elt->{handler}, " => ", + $res->{type}, "/", $res->{subtype}, "\n" ) + if $config->{debug}->{images}; + $res->{opts} = { %{ $res->{opts} // {} }, + %{ $elt->{opts} // {} } }; + $s->{assets}->{$id} = $res; + } + + # If the delegate produced an SVG, continue processing. + if ( $res && $res->{type} eq "image" && $res->{subtype} eq "svg" ) { + $elt = $res; + } + else { + # Proceed to next asset. + next; + } + } + + if ( $elt->{type} eq "image" && $elt->{subtype} eq "svg" ) { + warn("PDF: Preparing SVG image\n") if $config->{debug}->{images}; + require SVGPDF; + SVGPDF->VERSION(0.080); + + # One or more? + my $combine = ( !($elt->{opts}->{split}//1) + || $elt->{opts}->{id} + || defined($elt->{opts}->{spread}) ) + ? "stacked" : "none"; + my $sep = $elt->{opts}->{staffsep} || 0; + + # Note we need special font and text handlers. + my $p = SVGPDF->new + ( pdf => $ps->{pr}->{pdf}, + fc => sub { svg_fonthandler( $ps, @_ ) }, + tc => sub { svg_texthandler( $ps, @_ ) }, + atts => { debug => $config->{debug}->{svg} > 1, + verbose => $config->{debug}->{svg}, + } ); + my $data = $elt->{data}; + my $o = $p->process( $data ? \join( "\n", @$data ) : $elt->{uri}, + combine => $combine, + sep => $sep, + ); + warn( "PDF: Preparing SVG image => ", 0+@$o, " element", + @$o == 1 ? "" : "s", ", combine=$combine\n") + if $config->{debug}->{images}; + if ( ! @$o ) { + warn("Error in SVG embedding (no SVG objects found)\n"); + next; + } + + my $res = + $s->{assets}->{$id} = { + type => "image", + subtype => "xform", + width => $o->[0]->{width}, + height => $o->[0]->{height}, + vwidth => $o->[0]->{vwidth}, + vheight => $o->[0]->{vheight}, + data => $o->[0]->{xo}, + opts => { %{ $o->[0]->{opts} // {} }, + %{ $s->{assets}->{$id}->{opts} // {} }, + }, + sep => $sep, + }; + if ( @$o > 1 ) { + $res->{multi} = $o; + } + + warn("Created asset $id (xform, ", + $o->[0]->{vwidth}, "x", $o->[0]->{vheight}, ")", + " scale=", $res->{opts}->{scale} || 1, + " align=", $res->{opts}->{align}//"default", + " sep=", $sep, + "\n") + if $config->{debug}->{images}; + next; + } + + if ( $elt->{type} eq "image" ) { + warn("PDF: Preparing $elt->{subtype} image\n") if $config->{debug}->{images}; + if ( ($elt->{uri}//"") =~ /^chord:(.+)/ ) { + my $chord = $1; + # Look it up. + my $info = $s->{chordsinfo}->{$chord} + // ChordPro::Chords::known_chord($chord); + # If it is defined locally, merge. + for my $def ( @{ $s->{define} // [] } ) { + next unless $def->{name} eq $chord; + $info->{$_} = $def->{$_} for keys(%$def); + } + my $xo; + unless ( $info ) { + warn("Unknown chord in asset: $1\n"); + $xo = TextLayoutImageElement::alert(20); + } + else { + my $type = $elt->{opts}->{type} || $config->{instrument}->{type}; + my $p; + if ( $type eq "keyboard" ) { + require ChordPro::Output::PDF::KeyboardDiagram; + $p = ChordPro::Output::PDF::KeyboardDiagram->new( ps => $ps ); + } + else { + require ChordPro::Output::PDF::StringDiagram; + $p = ChordPro::Output::PDF::StringDiagram->new( ps => $ps ); + } + $xo = $p->diagram_xo($info); + } + my $res = + $s->{assets}->{$id} = { + type => "image", + subtype => "xform", + width => $xo->width, + height => $xo->height, + data => $xo, + maybe opts => $s->{assets}->{$id}->{opts}, + }; + warn("Created asset $id ($elt->{subtype}, ", + $res->{width}, "x", $res->{height}, ")", + map { " $_=" . $res->{opts}->{$_} } keys( %{$res->{opts}//{}} ), + "\n") + if $config->{debug}->{images}; + } + else { + if ( $elt->{uri} && !$elt->{data} ) { + $elt->{data} = loadblob($elt->{uri}); + } + my $data = $elt->{data} ? IO::String->new($elt->{data}) : $elt->{uri}; + my $img = $pr->{pdf}->image($data); + my $subtype = lc(ref($img) =~ s/^.*://r); + $subtype = "jpg" if $subtype eq "jpeg"; + my $res = + $s->{assets}->{$id} = { + type => "image", + subtype => $subtype, + width => $img->width, + height => $img->height, + data => $img, + maybe opts => $s->{assets}->{$id}->{opts}, + }; + warn("Created asset $id ($elt->{subtype}, ", + $res->{width}, "x", $res->{height}, ")", + map { " $_=" . $res->{opts}->{$_} } keys( %{$res->{opts}//{}} ), + "\n") + if $config->{debug}->{images}; + } + } + + next; + + if ( $elt->{type} eq "image" && $elt->{opts}->{spread} ) { + if ( $s->{spreadimage} ) { + warn("Ignoring superfluous spread image\n"); + } + else { + $s->{spreadimage} = $elt; + warn("PDF: Preparing images, got spread image\n") + if $config->{debug}->{images}; + next; # do not copy back + } + } + + } + warn("PDF: Preparing images, done\n") + if $config->{debug}->{images} || $config->{debug}->{assets}; + $assets = $s->{assets} || {}; + ::dump( $assets, as => "Assets, Pass 2" ) + if $config->{debug}->{assets} & 0x02; +} + + my %corefonts = ( ( map { lc($_) => $_ } @@ -3072,12 +2900,21 @@ sub is_corefont { sub svg_fonthandler { my ( $ps, $el, $pdf, $style ) = @_; - my $family = $style->{'font-family'}; - my $stl = $style->{'font-style'} // "normal"; - my $weight = $style->{'font-weight'} // "normal"; - my $size = $style->{'font-size'} || 12; - my $key = join( "|", $family, $stl, $weight ); + my $family = lc( $style->{'font-family'} ); + my $stl = lc( $style->{'font-style'} // "normal" ); + my $weight = lc( $style->{'font-weight'} // "normal" ); + my $size = $style->{'font-size'} || 12; + + # Font cache. state $fc = {}; + my $key = join( "|", $family, $stl, $weight ); + + # Clear cache when the PDF changes. + state $cf = ""; + if ( $cf ne $ps->{pr}->{pdf} ) { + $cf = $ps->{pr}->{pdf}; + $fc = {}; + } # As a special case we handle fonts with 'names' like # pdf.font.foo and map these to the corresponding font @@ -3173,18 +3010,106 @@ sub _dump { } } -package placeholder; -sub new { - my $class = shift; - my $self = {}; - $self->{name} = shift; - $self->{w} = shift; - $self->{h} = shift; - bless $self => $class; +use Object::Pad; + +class TextLayoutImageElement :isa(Text::Layout::PDFAPI2::ImageElement); + +use constant TYPE => "img"; +use Carp; + +use Text::ParseWords qw( shellwords ); + +method parse( $ctx, $k, $v ) { + + my %ctl = ( type => TYPE, size => $ctx->{size} ); + + # Split the attributes. + foreach my $kk ( shellwords($v) ) { + + # key=value + if ( $kk =~ /^([-\w]+)=(.+)$/ ) { + my ( $k, $v ) = ( $1, $2 ); + + # Ignore case unless required. + $v = lc $v unless $k =~ /^(id|chord)$/; + + if ( $k =~ /^(id|bbox|chord|src)$/ ) { + $ctl{$k} = $v; + } + elsif ( $k eq "align" && $v =~ /^(left|right|center)$/ ) { + $ctl{$k} = $v; + } + elsif ( $k eq "type" && $v =~ /^(strings?|keyboard)$/ ) { + $ctl{instrument} = $v; + } + elsif ( $k =~ /^(width|height|dx|dy|w|h)$/ ) { + $v = $1 * $ctx->{size} if $v =~ /^(-?[\d.]+)em$/; + $v = $1 * $ctx->{size} / 2 if $v =~ /^(-?[\d.]+)ex$/; + $v = $1 * $ctx->{size} / 100 if $v =~ /^(-?[\d.]+)\%$/; + $ctl{$k} = $v; + } + elsif ( $k =~ /^(scale)$/ ) { + $v = $1 / 100 if $v =~ /^([\d.]+)\%$/; + $ctl{$k} = $v; + } + else { + carp("Invalid " . TYPE . " attribute: \"$k\" ($kk)\n"); + } + } + + # Currently we do not have value-less attributes. + else { + carp("Invalid " . TYPE . " attribute: \"$kk\"\n"); + } + } + + return \%ctl; +} + +method getimage ($fragment) { + $fragment->{_img} //= do { + my $xo; + if ( $fragment->{id} ) { + $xo = ChordPro::Output::PDF::assets($fragment->{id})->{data}; + unless ( $xo ) { + warn("Unknown image ID in : $fragment->{id}\n"); + } + } + elsif ( $fragment->{chord} ) { + my $info = ChordPro::Chords::known_chord($fragment->{chord}); + unless ( $info ) { + warn("Unknown chord in : $fragment->{chord}\n"); + } + else { + my $p; + if ( ($fragment->{instrument} // $config->{instrument}->{type}) eq "keyboard" ) { + require ChordPro::Output::PDF::KeyboardDiagram; + $p = ChordPro::Output::PDF::KeyboardDiagram->new( ps => $ps ); + } + else { + require ChordPro::Output::PDF::StringDiagram; + $p = ChordPro::Output::PDF::StringDiagram->new( ps => $ps ); + } + $xo = $p->diagram_xo($info); + } + } + $xo // $self->SUPER::getimage($fragment) // alert( $fragment->{size} ); + }; +} + +sub alert ($size) { + my $scale = $size/20; + my $xo = $ps->{pr}->{pdf}->xo_form; + $xo->bbox( 0, -18*$scale, 20*$scale, 0 ); + $xo->matrix( $scale, 0, 0, -$scale, 0, 0 ); + $xo->line_width(2)->line_join(1); + $xo->stroke_color("red"); + $xo->fill_color("red"); + $xo->move( 1, 17 )->polyline( 19, 17, 10, 1 )->close->stroke; + $xo->rectangle( 9, 13, 11, 15 ); + $xo->move( 9, 12 )->polyline( 8.5, 7, 11.5, 7, 11, 12 )->close->fill; + return $xo; } -sub name { $_[0]->{name} } -sub width { $_[0]->{w} } -sub height { $_[0]->{h} } 1; diff --git a/lib/ChordPro/Output/PDF/Grid.pm b/lib/ChordPro/Output/PDF/Grid.pm new file mode 100644 index 00000000..4aefbe4c --- /dev/null +++ b/lib/ChordPro/Output/PDF/Grid.pm @@ -0,0 +1,311 @@ +#! perl + +package ChordPro::Output::PDF::Grid; + +use strict; +use warnings; +use Carp; +use feature 'state'; +use feature 'signatures'; +no warnings 'experimental::signatures'; + +sub gridline( $elt, $x, $y, $cellwidth, $barwidth, $margin, $ps, %opts ) { + + # Grid context. + + my $pr = $ps->{pr}; + my $fonts = $ps->{fonts}; + + # Use the chords font for the chords, and for the symbols size. + my $fchord = { %{ $fonts->{grid} || $fonts->{chord} } }; + delete($fchord->{background}); + $y -= font_bl($fchord); + + pr_label_maybe( $ps, $x, $y ); + + $x += $barwidth; + $cellwidth += $barwidth; + + $elt->{tokens} //= [ {} ]; + + my $firstbar; + my $lastbar; + foreach my $i ( 0 .. $#{ $elt->{tokens} } ) { + next unless is_bar( $elt->{tokens}->[$i] ); + $lastbar = $i; + $firstbar //= $i; + } + + my $prevbar = -1; + my @tokens = @{ $elt->{tokens} }; + my $t; + + if ( $margin->[0] ) { + $x -= $barwidth; + if ( $elt->{margin} ) { + my $t = $elt->{margin}; + if ( $t->{chords} ) { + $t->{text} = ""; + for ( 0..$#{ $t->{chords} } ) { + $t->{text} .= $t->{chords}->[$_]->chord_display . $t->{phrases}->[$_]; + } + } + $pr->text( $t->{text}, $x, $y, $fonts->{comment} ); + } + $x += $margin->[0] * $cellwidth + $barwidth; + } + + my $ctl = $pr->{ps}->{grids}->{cellbar}; + my $col = $pr->{ps}->{grids}->{symbols}->{color}; + my $needcell = $ctl->{width}; + + state $prevvoltastart; + my $align; + if ( $prevvoltastart && @tokens + && $tokens[0]->{class} eq "bar" && $tokens[0]->{align} ) { + $align = $prevvoltastart; + } + $prevvoltastart = 0; + + my $voltastart; + foreach my $i ( 0 .. $#tokens ) { + my $token = $tokens[$i]; + my $sz = $fchord->{size}; + + if ( $token->{class} eq "bar" ) { + $x -= $barwidth; + if ( $voltastart ) { + pr_voltafinish( $voltastart, $y, $x - $voltastart, $sz, $col, $pr ); + $voltastart = 0; + } + + $t = $token->{symbol}; + if ( 0 ) { + $t = "{" if $t eq "|:"; + $t = "}" if $t eq ":|"; + $t = "}{" if $t eq ":|:"; + } + else { + $t = "|:" if $t eq "{"; + $t = ":|" if $t eq "}"; + $t = ":|:" if $t eq "}{"; + } + + my $lcr = -1; # left, center, right + $lcr = 0 if $i > $firstbar; + $lcr = 1 if $i == $lastbar; + + if ( $t eq "|" ) { + if ( $token->{volta} ) { + if ( $align ) { + $x = $align; + $lcr = 0; + } + $voltastart = + pr_rptvolta( $x, $y, $lcr, $sz, $col, $pr, $token ); + $prevvoltastart ||= $x; + } + else { + pr_barline( $x, $y, $lcr, $sz, $col, $pr ); + } + } + elsif ( $t eq "||" ) { + pr_dbarline( $x, $y, $lcr, $sz, $col, $pr ); + } + elsif ( $t eq "|:" ) { + pr_rptstart( $x, $y, $lcr, $sz, $col, $pr ); + } + elsif ( $t eq ":|" ) { + pr_rptend( $x, $y, $lcr, $sz, $col, $pr ); + } + elsif ( $t eq ":|:" ) { + pr_rptendstart( $x, $y, $lcr, $sz, $col, $pr ); + } + elsif ( $t eq "|." ) { + pr_endline( $x, $y, $lcr, $sz, $col, $pr ); + } + elsif ( $t eq " %" ) { # repeat2Bars + pr_repeat( $x+$sz/2, $y, 0, $sz, $col, $pr ); + } + else { + die($t); # can't happen + } + $x += $barwidth; + $prevbar = $i; + $needcell = 0; + next; + } + + if ( $token->{class} eq "repeat2" ) { + # For repeat2Bars, change the next bar line to pseudo-bar. + my $k = $prevbar + 1; + while ( $k <= $#tokens + && !is_bar($tokens[$k]) ) { + $k++; + } + $tokens[$k] = { symbol => " %", class => "bar" }; + $x += $cellwidth; + $needcell = 0; + next; + } + + pr_cellline( $x-$barwidth, $y, 0, $sz, $ctl->{width}, + $pr->_fgcolor($ctl->{color}), $pr ) + if $needcell; + $needcell = $ctl->{width}; + + if ( $token->{class} eq "chord" || $token->{class} eq "chords" ) { + my $tok = $token->{chords} // [ $token->{chord} ]; + my $cellwidth = $cellwidth / @$tok; + for my $t ( @$tok ) { + $x += $cellwidth, next if $t eq ''; + $t = $t->chord_display; + $pr->text( $t, $x, $y, $fchord ); + $x += $cellwidth; + } + } + elsif ( exists $token->{chord} ) { + # I'm not sure why not testing for class = chord... + warn("Chord token without class\n") + unless $token->{class} eq "chord"; + my $t = $token->{chord}; + $t = $t->chord_display; + $pr->text( $t, $x, $y, $fchord ) + unless $token eq "."; + $x += $cellwidth; + } + elsif ( $token->{class} eq "slash" ) { + $pr->text( "/", $x, $y, $fchord ); + $x += $cellwidth; + } + elsif ( $token->{class} eq "space" ) { + $x += $cellwidth; + } + elsif ( $token->{class} eq "repeat1" ) { + $t = $token->{symbol}; + my $k = $prevbar + 1; + while ( $k <= $#tokens + && !is_bar($tokens[$k]) ) { + $k++; + } + pr_repeat( $x + ($k - $prevbar - 1)*$cellwidth/2, $y, + 0, $fchord->{size}, $col, $pr ); + $x += $cellwidth; + } + if ( $x > $ps->{papersize}->[0] ) { + # This should be signalled by the parser. + # warn("PDF: Too few cells for content\n"); + last; + } + } + + if ( $margin->[1] && $elt->{comment} ) { + my $t = $elt->{comment}; + if ( $t->{chords} ) { + $t->{text} = ""; + for ( 0..$#{ $t->{chords} } ) { + $t->{text} .= $t->{chords}->[$_] . $t->{phrases}->[$_]; + } + } + $pr->text( " " . $t->{text}, $x, $y, $fonts->{comment} ); + } +} + +sub is_bar( $elt ) { + exists( $elt->{class} ) && $elt->{class} eq "bar"; +} + +sub pr_cellline( $x, $y, $lcr, $sz, $w, $col, $pr ) { + $x -= $w / 2 * ($lcr + 1); + $pr->vline( $x, $y+0.9*$sz, $sz, $w, $col ); +} + +sub pr_barline( $x, $y, $lcr, $sz, $col, $pr ) { + my $w = $sz / 10; # glyph width = $w + $x -= $w / 2 * ($lcr + 1); + $pr->vline( $x, $y+0.9*$sz, $sz, $w, $col ); +} + +sub pr_dbarline( $x, $y, $lcr, $sz, $col, $pr ) { + my $w = $sz / 10; # glyph width = 3 * $w + $x -= 1.5 * $w * ($lcr + 1); + $pr->vline( $x, $y+0.9*$sz, $sz, $w, $col ); + $x += 2 * $w; + $pr->vline( $x, $y+0.9*$sz, $sz, $w, $col ); +} + +sub pr_rptstart( $x, $y, $lcr, $sz, $col, $pr ) { + my $w = $sz / 10; # glyph width = 3 * $w + $x -= 1.5 * $w * ($lcr + 1); + $pr->vline( $x, $y+0.9*$sz, $sz, $w, $col ); + $x += 2 * $w; + $y += 0.55 * $sz; + $pr->line( $x, $y, $x, $y+$w, $w, $col ); + $y -= 0.4 * $sz; + $pr->line( $x, $y, $x, $y+$w, $w, $col ); +} + +sub pr_rptvolta( $x, $y, $lcr, $sz, $symcol, $pr, $token ) { + my $w = $sz / 10; # glyph width = 3 * $w + my $col = $pr->{ps}->{grids}->{volta}->{color}; + my $ret = $x -= 1.5 * $w * ($lcr + 1); + $pr->vline( $x, $y+0.9*$sz, $sz, $w, $col ); + $x += 2 * $w; + my $font = $pr->{ps}->{fonts}->{grid}; + $pr->setfont($font); + $pr->text( "" . $token->{volta} . "", + $x-$w/2, $y, $font ); + $ret; +} + +sub pr_voltafinish( $x, $y, $width, $sz, $symcol, $pr ) { + my $w = $sz / 10; # glyph width = 3 * $w + my ( $col, $span ) = @{$pr->{ps}->{grids}->{volta}}{qw(color span)}; + $pr->hline( $x, $y+0.9*$sz+$w/4, $width*$span, $w/2, $col ); +} + +sub pr_rptend( $x, $y, $lcr, $sz, $col, $pr ) { + my $w = $sz / 10; # glyph width = 3 * $w + $x -= 1.5 * $w * ($lcr + 1); + $pr->vline( $x + 2*$w, $y+0.9*$sz, $sz, $w, $col ); + $y += 0.55 * $sz; + $pr->line( $x, $y, $x, $y+$w, $w, $col ); + $y -= 0.4 * $sz; + $pr->line( $x, $y, $x, $y+$w, $w, $col ); +} + +sub pr_rptendstart( $x, $y, $lcr, $sz, $col, $pr ) { + my $w = $sz / 10; # glyph width = 5 * $w + $x -= 2.5 * $w * ($lcr + 1); + $pr->vline( $x + 2*$w, $y+0.9*$sz, $sz, $w, $col ); + $y += 0.55 * $sz; + $pr->line( $x, $y, $x , $y+$w, $w, $col ); + $pr->line( $x+4*$w, $y, $x+4*$w, $y+$w, $w, $col ); + $y -= 0.4 * $sz; + $pr->line( $x, $y, $x, $y+$w, $w, $col ); + $pr->line( $x+4*$w, $y, $x+4*$w, $y+$w, $w, $col ); +} + +sub pr_repeat( $x, $y, $lcr, $sz, $col, $pr ) { + my $w = $sz / 3; # glyph width = 3 * $w + $x -= 1.5 * $w * ($lcr + 1); + my $lw = $sz / 10; + $x -= $w / 2; + $pr->line( $x, $y+0.2*$sz, $x + $w, $y+0.7*$sz, $lw ); + $pr->line( $x, $y+0.6*$sz, $x + 0.07*$sz , $y+0.7*$sz, $lw ); + $x += $w; + $pr->line( $x - 0.05*$sz, $y+0.2*$sz, $x + 0.02*$sz, $y+0.3*$sz, $lw ); +} + +sub pr_endline( $x, $y, $lcr, $sz, $col, $pr ) { + my $w = $sz / 10; # glyph width = 2 * $w + $x -= 0.75 * $w * ($lcr + 1); + $pr->vline( $x, $y+0.85*$sz, 0.9*$sz, 2*$w ); +} + +################ Hooks ################ + +*font_bl = *ChordPro::Output::PDF::font_bl; +*pr_label_maybe = *ChordPro::Output::PDF::pr_label_maybe; + +1; diff --git a/lib/ChordPro/Output/PDF/KeyboardDiagram.pm b/lib/ChordPro/Output/PDF/KeyboardDiagram.pm new file mode 100644 index 00000000..158aef1b --- /dev/null +++ b/lib/ChordPro/Output/PDF/KeyboardDiagram.pm @@ -0,0 +1,305 @@ +#! perl + +use v5.26; +use Object::Pad; +use utf8; + +my $dcache; # cache core grids +my $pdf = ""; # for cache flush + +class ChordPro::Output::PDF::KeyboardDiagram; + +field $ps :param; + +field $config; +field $pr; + +field $kw; +field $kh; +field $lw; +field $fg; +field $keys; +field $base; +field $base_k; +field $show; +field $pressed; + +ADJUST { + $config = $::config; + $pr = $ps->{pr}; + my $ctl = $ps->{kbdiagrams}; + $kw = $ctl->{width} || 6; + $kh = $ctl->{height} || 6; + $lw = ($ctl->{linewidth} || 0.10) * $kw; + $keys = $ctl->{keys}; + $base = $ctl->{base}; + $show = $ctl->{show}; + $pressed = $ctl->{pressed}; + $dcache = {} if $pr->{pdf} ne $pdf; + $pdf = $pr->{pdf}; + + unless ( $keys =~ /^(?:7|10|14|17|21)$/ ) { + die("pdf.kbdiagrams.keys is $keys, must be one of 7, 10, 14, 17, or 21\n"); + } + + unless ( $base =~ /^(?:C|F)$/i ) { + die("pdf.kbdiagrams.base is \"$base\", must be \"C\" or \"F\"\n"); + } + if ( uc($base) eq 'C' ) { + $base = $base_k = 0; + } + else { # must be 'F' + $base_k = 3; + $base = 5; + } + + unless ( $show =~ /^(?:top|bottom|right|below)$/i ) { + die("pdf.kbdiagrams.show is \"$show\", must be one of ". + "\"top\", \"bottom\", \"right\", or \"below\"\n"); + } +} + +use constant DIAG_DEBUG => 0; + +# The vertical space the diagram requires. +method vsp0 ( $elt, $dummy = 0 ) { + $ps->{fonts}->{diagram}->{size} * 1.2 + $kh + $lw; +} + +# The advance height. +method vsp1 ( $elt, $dummy = 0 ) { + $ps->{kbdiagrams}->{vspace} * $kh; +} + +# The vertical space the diagram requires, including advance height. +method vsp ( $elt, $dummy = 0 ) { + $self->vsp0($elt) + $self->vsp1($elt); +} + +# The horizontal space the diagram requires. +method hsp0 ( $elt, $dummy = 0 ) { + $lw + $keys * $kw; +} + +# The advance width. +method hsp1 ( $elt, $dummy = 0 ) { + $ps->{kbdiagrams}->{hspace} * $kw; +} + +# The horizontal space the diagram requires, including advance width. +method hsp ( $elt, $dummy = 0 ) { + $self->hsp0($elt) + $self->hsp1($ps); +} + +sub font_bl ($font) { + &ChordPro::Output::PDF::font_bl($font); +} + +my %keytypes = + ( 0 => [0,"L"], # Left + 1 => [0,"B"], # Black + 2 => [1,"M"], # Middle + 3 => [1,"B"], + 4 => [2,"R"], # Right + 5 => [3,"L"], + 6 => [3,"B"], + 7 => [4,"M"], + 8 => [4,"B"], + 9 => [5,"M"], + 10 => [5,"B"], + 11 => [6,"R"] ); + + +# The actual draw method. +method draw ( $info, $x, $y, $dummy = 0 ) { + return unless $info; + my $w = $lw + $kw * $keys; + $fg = $info->{diagram} // "black"; # $ps->{theme}->{foreground}; + + # Get (or infer) keys. + my @keys = @{ChordPro::Chords::get_keys($info)}; + unless ( @keys ) { + warn("PDF: No diagram for chord \"", $info->name, "\"\n"); + } + + my $font = $ps->{fonts}->{diagram}; + + my $xo = $self->diagram_xo($info); + my @bb = $xo->bbox; + warn("BB [ @bb ] $x $y\n") if DIAG_DEBUG; + $pr->{pdfgfx}->object( $xo, $x, + $y - ($font->{size} * 1.2 + $lw) ); + + # Draw font name. + $pr->setfont($font); + my $name = $info->chord_display; + $name .= "?" unless @keys; + $name = "$name" + if $info->{diagram}; + $pr->text( $name, $x + ($w - $pr->strwidth($name))/2, $y - font_bl($font) ); +} + +# Returns the complete diagram as an xo. This includes the core grid +# and pressed keys. +# Bounding box origin is top left of the grid. +# Note that the chord name is not part of the diagram. + +method diagram_xo ($info) { + return unless $info; + + my $col = $pressed // "red"; + $fg = $info->{diagram} // "black"; # $ps->{theme}->{foreground}; + my $w = $lw + $kw * $keys; + my $v = $kh; + + # Get (or infer) keys. + my @keys = @{ChordPro::Chords::get_keys($info)}; + unless ( @keys ) { + warn("PDF: No diagram for chord \"", $info->name, "\"\n"); + } + + my $xo = $pdf->xo_form; + + # Draw the core grid. + $xo->line_width($lw); + my $xg = $self->grid_xo; + $xo->bbox( $xg->bbox ); + $xo->object( $xg, 0, 0, 1 ); + + my $kk = ( $keys % 7 == 0 ) + ? 12 * int( $keys / 7 ) + : $keys == 10 ? 17 : 29; + + # Vertical offsets in the key image. + my $t = 0; + my $m = $t - $kh / 2; + my $b = $t - $kh; + + # Horizontal offsets in the key image. + my $l = 0; + my $ml = $l + 1 * $kw / 3; + my $mr = $l + 2 * $kw / 3; + my $r = $l + $kw; # 3 * $kw / 3; + my $xr = $l + 4 * $kw / 3; + + # Don't use theme colour, use black & white. + $xo->stroke_color($fg); + $xo->fill_color($col); + + # Shift down if would start in 2nd octave. + my $kd = -int(($keys[0] + $info->{root_ord}) / 12) * 12; + # Adjust for diagram start. + $kd+=12 if ($keys[0] + $info->{root_ord}) < $base; + + for my $key ( @keys ) { + $key += $kd + $info->{root_ord}; + $key += 12 if $key < 0; + $key -= 12 while $key >= $kk; + # Get octave and reduce. + my $o = int( $key / 12 ); # octave + $key %= 12; + + # Get the key type. + my ($pos,$type) = @{$keytypes{$key}}; + + # Adjust for diagram start. + $pos -= $base_k; + + # Reduce to single octave and scale. + $pos += 7, $o-- while $pos < 0; + $pos %= 7; + $pos += 7 * $o if $o >= 1; + + # Actual displacement. + my $pkw = $pos * $kw; + + # Draw the keys. + if ( $type eq "L" ) { + $xo->move( $pkw + $l, $b ); + $xo->polyline( $pkw + $l, $t, + $pkw + $mr, $t, + $pkw + $mr, $m, + $pkw + $r, $m, + $pkw + $r, $b )->close->fillstroke; + } + elsif ( $type eq "R" ) { + $xo->move( $pkw + $l, $b ); + $xo->polyline( $pkw + $l, $m, + $pkw + $ml, $m, + $pkw + $ml, $t, + $pkw + $r, $t, + $pkw + $r, $b )->close->fillstroke; + } + elsif ( $type eq "M" ) { + $xo->move( $pkw + $l, $b ); + $xo->polyline( $pkw + $l, $m, + $pkw + $ml, $m, + $pkw + $ml, $t, + $pkw + $mr, $t, + $pkw + $mr, $m, + $pkw + $r, $m, + $pkw + $r, $b )->close->fillstroke; + } + else { + $xo->rectangle( $pkw + $mr, $m, + $pkw + $xr, $t )->fillstroke; + } + } +# $xo->fillstroke; + $xo; +} + +use constant DIAG_GRID_XO => 0; + +method grid_xo { + + return $dcache->{$kw,$kh,$fg,$lw,$keys} //= do { + + my $w = $kw * $keys; # total width, excl linewidth + my $h = $kh; # total height, excl linewidth + + my $xo = $pdf->xo_form; + + # Bounding box must take linewidth into account. + # Origin is top left, so y runs negative. + my $xp = DIAG_GRID_XO ? 2 : 0; + my @bb = ( -$lw/2 - $xp, $lw/2 + $xp, + $w + $lw/2 + $xp, -($h + $lw/2) - $xp ); + $xo->bbox(@bb); + $xo->line_width($lw); + + if ( DIAG_GRID_XO ) { + # Draw the grid. + $xo->fill_color('yellow'); + $xo->rectangle(@bb)->fill; + } + + $xo->stroke_color($fg); + $xo->fill_color($fg); + + $xo->rectangle( 0, 0, $w, -$h ); + for ( 1 .. $keys-1 ) { + $xo->move( $_*$kw, 0 ); + $xo->vline(-$h); + } + $xo->stroke; + for my $i ( 1, 2, 4, 5, 6, 8, 9, 11, + 12, 13, 15, 16, 18, 19, 20, 22, 23 ) { + next if $i < $base_k; + last if $i > $keys + $base_k; + my $x = ($i-$base_k-1)*$kw+2*$kw/3; + $xo->rectangle( $x, -$kh/2, $x + 2*$kw/3, 0 )->fillstroke; + } + + if ( DIAG_GRID_XO ) { + # Show origin. + $xo->fill_color('red'); + my $lw = $lw/2; + $xo->rectangle( -$lw, -$lw, $lw, $lw )->fill; + } + + $xo; + }; +} + +1; diff --git a/lib/ChordPro/Output/PDF/KeyboardDiagrams.pm b/lib/ChordPro/Output/PDF/KeyboardDiagrams.pm deleted file mode 100644 index 46996992..00000000 --- a/lib/ChordPro/Output/PDF/KeyboardDiagrams.pm +++ /dev/null @@ -1,270 +0,0 @@ -#! perl - -use strict; - -package main; - -our $config; - -package ChordPro::Output::PDF::KeyboardDiagrams; - -sub new { - my ( $pkg, $ps ) = @_; - - my $ctl = $ps->{kbdiagrams}; - my $kw = $ctl->{width}; - my $kh = $ctl->{height}; - - my $keys = $ctl->{keys}; - unless ( $keys =~ /^(?:7|10|14|17|21)$/ ) { - die("pdf.kbdiagrams.keys is $keys, must be one of 7, 10, 14, 17, or 21\n"); - } - - my $base = $ctl->{base}; - unless ( $base =~ /^(?:C|F)$/i ) { - die("pdf.kbdiagrams.base is \"$base\", must be \"C\" or \"F\"\n"); - } - - my $show = $ctl->{show}; - unless ( $show =~ /^(?:top|bottom|right|below)$/i ) { - die("pdf.kbdiagrams.show is \"$show\", must be one of ". - "\"top\", \"bottom\", \"right\", or \"below\"\n"); - } - - bless {} => $pkg; -} - -# The vertical space the diagram requires. -sub vsp0 { - my ( $self, $elt, $ps ) = @_; - my $ctl = $ps->{kbdiagrams}; - my $kw = $ctl->{width}; - my $kh = $ctl->{height}; - my $lw = ($ctl->{linewidth} || 0.10) * $kw; - $ps->{fonts}->{diagram}->{size} * 1.2 + $kh + $lw; -} - -# The advance height. -sub vsp1 { - my ( $self, $elt, $ps ) = @_; - my $ctl = $ps->{kbdiagrams}; - $ctl->{vspace} * $ctl->{height}; -} - -# The vertical space the diagram requires, including advance height. -sub vsp { - my ( $self, $elt, $ps ) = @_; - $self->vsp0( $elt, $ps ) + $self->vsp1( $elt, $ps ); -} - -# The horizontal space the diagram requires. -sub hsp0 { - my ( $self, $elt, $ps ) = @_; - my $ctl = $ps->{kbdiagrams}; - my $kw = $ctl->{width}; - my $kh = $ctl->{height}; - my $lw = ($ctl->{linewidth} || 0.10) * $kw; - $lw + $ctl->{keys} * $kw; -} - -# The advance width. -sub hsp1 { - my ( $self, $elt, $ps ) = @_; - my $ctl = $ps->{kbdiagrams}; - my $kw = $ctl->{width}; - $ctl->{hspace} * $kw; -} - -# The horizontal space the diagram requires, including advance width. -sub hsp { - my ( $self, $elt, $ps ) = @_; - $self->hsp0( $elt, $ps ) + $self->hsp1( $elt, $ps ); -} - -sub font_bl { - goto &ChordPro::Output::PDF::font_bl; -} - -my %keytypes = - ( 0 => [0,"L"], # Left - 1 => [0,"B"], # Black - 2 => [1,"M"], # Middle - 3 => [1,"B"], - 4 => [2,"R"], # Right - 5 => [3,"L"], - 6 => [3,"B"], - 7 => [4,"M"], - 8 => [4,"B"], - 9 => [5,"M"], - 10 => [5,"B"], - 11 => [6,"R"] ); - - -# The actual draw method. -sub draw { - my ( $self, $info, $x, $y, $ps ) = @_; - return unless $info; - my $x0 = $x; - - # Get (or infer) keys. - my @keys = @{ChordPro::Chords::get_keys($info)}; - unless ( @keys ) { - warn("PDF: No diagram for chord \"", $info->name, "\"\n"); - } - - my $ctl = $ps->{kbdiagrams}; - my $kw = $ctl->{width}; - my $kh = $ctl->{height}; - my $lw = ($ctl->{linewidth} || 0.10) * $kw; - my $keys = $ctl->{keys}; - my $col = $ctl->{pressed} // "red"; - my $pr = $ps->{pr}; - my $w = $lw + $kw * $keys; - my $v = $kh; - - # Draw font name. - my $font = $ps->{fonts}->{diagram}; - $pr->setfont($font); - my $name = $info->chord_display; - $name .= "?" unless @keys; - # $name .= "*" - # unless $info->{origin} ne "user" - # || $ps->{kbdiagrams}->{show} eq "user"; - $pr->text( $name, $x + ($w - $pr->strwidth($name))/2, $y - font_bl($font) ); - $y -= $font->{size} * 1.2 + $lw; - - # Draw the grid. - my $xo = $self->grid_xo( $ps, $lw ); - $pr->{pdfgfx}->formimage( $xo, $x, $y-$v, 1 ); - - my $kk = ( $keys % 7 == 0 ) - ? 12 * int( $keys / 7 ) - : $keys == 10 ? 17 : 29; - - # Vertical offsets in the key image. - my $t = $y; - my $m = $y - $kh / 2; - my $b = $y - $kh; - - # Horizontal offsets in the key image. - my $l = $x; - my $ml = $x + 1 * $kw / 3; - my $mr = $x + 2 * $kw / 3; - my $r = $x + $kw; # 3 * $kw / 3; - my $xr = $x + 4 * $kw / 3; - - # Don't use theme colour, use black & white. - my $fgcol = "black"; # $ps->{theme}->{foreground}; - - # Shift down if would start in 2nd octave. - my $kd = -int(($keys[0] + $info->{root_ord}) / 12) * 12; - - for my $key ( @keys ) { - $key += $kd + $info->{root_ord}; - $key += 12 if $key < 0; - $key -= 12 while $key >= $kk; - # Get octave and reduce. - my $o = int( $key / 12 ); # octave - $key %= 12; - - # Get the key type. - my ($pos,$type) = @{$keytypes{$key}}; - - # Adjust for diagram start. - $pos -= 3 if uc($ctl->{base}) ne 'C'; # must be 'F' - - # Reduce to single octave and scale. - $pos += 7, $o-- while $pos < 0; - $pos %= 7; - $pos += 7 * $o if $o >= 1; - - # Actual displacement. - my $pkw = $pos * $kw; - - # Draw the keys. - if ( $type eq "L" ) { - $pr->poly( [ $pkw + $l, $b, - $pkw + $l, $t, - $pkw + $mr, $t, - $pkw + $mr, $m, - $pkw + $r, $m, - $pkw + $r, $b ], - $lw, $col, $fgcol ); - } - elsif ( $type eq "R" ) { - $pr->poly( [ $pkw + $l, $b, - $pkw + $l, $m, - $pkw + $ml, $m, - $pkw + $ml, $t, - $pkw + $r, $t, - $pkw + $r, $b ], - $lw, $col, $fgcol ); - } - elsif ( $type eq "M" ) { - $pr->poly( [ $pkw + $l, $b, - $pkw + $l, $m, - $pkw + $ml, $m, - $pkw + $ml, $t, - $pkw + $mr, $t, - $pkw + $mr, $m, - $pkw + $r, $m, - $pkw + $r, $b - ], - $lw, $col, $fgcol ); - } - else { - $pr->rectxy( $pkw + $mr, $m, - $pkw + $xr, $t, - $lw, $col, $fgcol ); - } - } - - return $w + $ctl->{hspace}; -} - -sub grid_xo { - my ( $self, $ps, $lw ) = @_; - my $ctl = $ps->{kbdiagrams}; - my $kw = $ctl->{width}; - my $kh = $ctl->{height}; - $lw //= ($ctl->{linewidth} || 0.10) * $kw; - my $keys = $ctl->{keys}; - my $base = uc($ctl->{base}) eq "F" ? 3 : 0; - # Don't use theme colour, use black & white. - my $fgcol = "black"; # $ps->{theme}->{foreground}; - - return $self->{grids}->{$kw,$kh,$lw,$keys} //= do - { - my $w = $kw * $keys; # total width, excl linewidth - my $h = $kh; # total height, excl linewidth - - my $form = $ps->{pr}->{pdf}->xo_form; - - # Bounding box must take linewidth into account. - my @bb = ( -$lw/2, -$lw/2, $w+$lw/2, $h+$lw/2 ); - $form->bbox(@bb); - - # Pseudo-object to access low level drawing routines. - my $dc = bless { pdfgfx => $form } => - ChordPro::Output::PDF::Writer::; - - # Draw the grid. - $dc->rectxy( @bb, 0, 'yellow' ) if 0; - $dc->rectxy( @bb, 0, 'white' ) - unless $ps->{theme}->{background} =~ /^white|none|#ffffff$/i; - $dc->rectxy( 0, 0, $w, $kh, $lw, undef, $fgcol ); - $dc->vline( $_*$kw, $kh, $kh, $lw, $fgcol ) for 1..$keys-1; - for my $i ( 1, 2, 4, 5, 6, 8, 9, 11, 12, 13, 15, 16, 18, 19, 20, 22, 23 ) { - next if $i < $base; - last if $i > $keys + $base; - my $x = ($i-$base-1)*$kw+2*$kw/3; - $dc->rectxy( $x, $kh/2, $x + 2*$kw/3, $kh, - $lw, $fgcol, - $fgcol ); - } - - $form; - }; -} - -1; diff --git a/lib/ChordPro/Output/PDF/StringDiagram.pm b/lib/ChordPro/Output/PDF/StringDiagram.pm new file mode 100644 index 00000000..43dd1971 --- /dev/null +++ b/lib/ChordPro/Output/PDF/StringDiagram.pm @@ -0,0 +1,385 @@ +#! perl + +use v5.26; +use Object::Pad; +use utf8; + +my $dcache; # cache core grids +my $pdf = ""; # for cache flush + +class ChordPro::Output::PDF::StringDiagram; + +field $ps :param; + +field $config; +field $pr; + +field $gw; # width of a cell, pt +field $gh; # height of a cell, pt +field $lw; # fraction of cell width +field $nutwidth; # width (in linewidth) of the top nut +field $nw; # extra width for the top nut, pt +field $vc; # cells, vertical +field $strings; # number of strings +field $hc; # cells, horizontal (= strings) +field $dot; # dot size, fraction of cell width +field $bsz; # barre size, fraction of dot +field $fsh; # show fingers (0, 1, "below") +field $fg; # foreground color + +ADJUST { + $config = $::config; + $strings = $config->diagram_strings; + $pr = $ps->{pr}; + my $ctl = $ps->{diagrams}; + $gw = $ctl->{width} || 6; + $gh = $ctl->{height} || 6; + $lw = ($ctl->{linewidth} || 0.10) * $gw; + $nutwidth = $ctl->{nutwidth} || 1; + $nw = ($nutwidth-1) * $lw; + $vc = $ctl->{vcells} || 4; + $hc = $strings; + $dot = $ctl->{dotsize} * ( $gh < $gw ? $gh : $gw ); + $bsz = $ctl->{barwidth} * $dot; + $fsh = $ctl->{fingers} || 0; + $dcache = {} if $pr->{pdf} ne $pdf; + $pdf = $pr->{pdf}; +} + +use constant DIAG_DEBUG => 0; + +# The vertical space the diagram requires. +method vsp0( $elt, $dummy = 0 ) { + $ps->{fonts}->{diagram}->{size} * $ps->{spacing}->{diagramchords} + + $nutwidth * $lw + 0.40 * $gw + + $vc * $gh + + ( $fsh eq "below" ? $ps->{fonts}->{diagram}->{size} : 0 ) + ; +} + +# The advance height. +method vsp1( $elt, $dummy = 0 ) { + $ps->{diagrams}->{vspace} * $gh; +} + +# The vertical space the diagram requires, including advance height. +method vsp( $elt, $dummy = 0 ) { + $self->vsp0($elt) + $self->vsp1($elt); +} + +# The horizontal space the diagram requires. +method hsp0( $elt, $dummy = 0 ) { + ($strings - 1) * $gw; +} + +# The advance width. +method hsp1( $elt, $dummy = 0 ) { + $ps->{diagrams}->{hspace} * $gw; +} + +# The horizontal space the diagram requires, including advance width. +method hsp( $elt, $dummy = 0 ) { + $self->hsp0($elt) + $self->hsp1($elt); +} + +sub font_bl ($font) { + ChordPro::Output::PDF::font_bl($font); +} + +# The actual draw method. +method draw( $info, $x, $y, $dummy=0 ) { + return unless $info; + + my $font = $ps->{fonts}->{diagram}; + + my $xo = $self->diagram_xo($info); + my @bb = $xo->bbox; + warn("BB [ @bb ] $x $y\n") if DIAG_DEBUG; + $pr->{pdfgfx}->object( $xo, $x, + + $y - ($font->{size} * $ps->{spacing}->{diagramchords} + $dot/2 + $lw) ); + + # Draw name. + my $w = $gw * ($strings - 1); + $pr->setfont($font); + my $name = $info->chord_display; + $name = "$name" + if $info->{diagram}; + $pr->text( $name, $x + ($w - $pr->strwidth($name))/2, $y-font_bl($font) ); +} + +# Returns the complete diagram as an xo. This includes the core grid, +# finger/fret positions, open and muted string indicators. +# The bounding box includes space form the open and muted string indicators +# and dots on the first and last strings, even when absent. +# The bbox includes basefret and fingers (below) if present. +# Origin is top left of the grid. +# Note that the chord name is not part of the diagram. + +method diagram_xo( $info ) { + return unless $info; + $fg = $info->{diagram} // $config->{pdf}->{theme}->{foreground}; + + my $x = 0; + my $w = $gw * ($strings - 1); + my $baselabeloffset = $info->{baselabeloffset} || 0; + my $basefretno = $info->{base} + $baselabeloffset; + my $basefont; # for base label + my $basesize; # for base label + + # Get the core grid. + my $xg = $self->grid_xo; + my @xgbb = $xg->bbox; + + my $xo = $pdf->xo_form; + my @bb = ( 0, + 0.77 * $dot + 2*$lw, + $w + $dot/2, + $xgbb[3] ); + + if ( $basefretno > 1 ) { + $basefont = $ps->{fonts}->{diagram_base}->{fd}->{font}; + $basesize = $ps->{spacing}->{diagramchords}*$gh; + $bb[0] -= $basefont->width("$basefretno ") * $basesize; + } + else { + $bb[0] -= $dot/2; + } + if ( $fsh eq "below" && $info->{fingers} ) { + $bb[3] -= $gh + $lw; + } + $xo->bbox(@bb); + $xo->line_width($lw); + $xo->stroke_color($fg); + $xo->fill_color($fg); + + if ( DIAG_DEBUG ) { + # Draw the grid. + $xo->save; + $xo->fill_color('yellow'); + $xo->rectangle($xo->bbox)->fill; + $xo->object( $xg, 0, 0, 1 ); + $xo->fill_color('red'); + my $lw = $lw/2; + $xo->rectangle( -$lw, -$lw, $lw, $lw )->fill; + $xo->restore; + } + else { + $xo->object( $xg, 0, 0, 1 ); + } + + # Draw extended nut if base = 1. + if ( $info->{base} <= 1 ) { + if ( $nutwidth > 1 ) { + for ( 0 .. $nutwidth-2 ) { + $xo->move( -$lw/2, -$_*$lw ); + $xo->hline( $w + $lw/2 ); + } + $xo->stroke; + } + } + + # Draw first fret number, if > 1. + if ( $basefretno > 1 ) { + my $i = sprintf("%2d ", $basefretno ); + $xo->textstart; + $xo->font( $basefont, $basesize ); + $xo->translate( 0, -$nw - ($baselabeloffset+0.85)*$gh ); + $xo->text( "$basefretno ", align => "right" ); + $xo->textend; + } + + my $fingers; + $fingers = $info->{fingers} if $fsh; + + # Bar detection. + my $bar = {}; + if ( $fingers ) { + my %h; + my $str = 0; + my $got = 0; + foreach ( @{ $fingers } ) { + $str++, next unless $info->{frets}->[$str] > 0; + if ( $bar->{$_} ) { + # Same finger on multiple strings -> bar. + $got++; + $bar->{$_}->[-1] = $str; + } + else { + # Register. + $bar->{$_} = [ $_, $info->{frets}->[$str], $str, $str ]; + } + $str++; + } + if ( $got ) { + $xo->save; + $xo->line_width($bsz)->line_cap(0); + foreach ( sort keys %$bar ) { + my @bi = @{ $bar->{$_} }; + if ( $bi[-2] == $bi[-1] ) { # not a bar + delete $bar->{$_}; + next; + } + # Print the bar line. + $x = $bi[2]*$gw; + $xo->move( $x, -$nw -$bi[1]*$gh+$gh/2 ); + $xo->hline( $x+($bi[3]-$bi[2])*$gw); + } + $xo->stroke->restore; + } + } + + my $oflo; # to detect out of range frets + + $x = -$gw; + for my $sx ( 0 .. $strings-1 ) { + $x += $gw; + my $fret = $info->{frets}->[$sx]; + my $fing = -1; + $fing = $fingers->[$sx] // -1 if $fingers; + + # For bars, only the first and last finger. + if ( $fing && $bar->{$fing} ) { + next unless $sx == $bar->{$fing}->[2] || $sx == $bar->{$fing}->[3]; + } + + if ( $fret > 0 ) { + if ( $fret > $vc && !$oflo++ ) { + warn("Diagram $info->{name}: ", + "Fret position $fret exceeds diagram size $vc\n"); + next; + } + $xo->circle( $x, -$nw - ($fret-0.5)*$gh, $dot/2 )->fill; + + } + elsif ( $fret < 0 ) { + $xo->move( $x - $dot/3, 0.77 * $dot + $lw ); + $xo->line( $x + $dot/3, 0.1 * $gh + $lw ); + $xo->move( $x + $dot/3, 0.77 * $dot + $lw ); + $xo->line( $x - $dot/3, 0.1 * $gh + $lw ); + $xo->stroke; + } + elsif ( $info->{base} > 0 ) { + $xo->circle( $x, 3.5*$gh/10 + $lw, $dot/3 )->stroke; + } + } + + # Show the fingers, if any. + if ( $fingers && @$fingers ) { + my $font = $ps->{fonts}->{diagram}->{fd}->{font}; + my $size = $dot; + my $asc; # space if "below" + + # Color of the numbers. + my $fbg = ""; + unless ( $fsh eq "below" ) { + # The numbercolor property of the chordfingers is used for the + # color of the dot numbers. + my $fcf = $ps->{fonts}->{chordfingers}; + $fbg = $pr->_bgcolor($fcf->{numbercolor}); + # However, if none we should really use white. + $fbg = "white" if $fbg eq "none"; + } + + $x = -$gw; + my $did = 0; + for my $sx ( 0 .. $strings-1 ) { + last if $fbg eq $fg; + $x += $gw; + my $fret = $info->{frets}->[$sx]; + next unless $fret > 0; + my $fing = $fingers->[$sx]; + next unless $fing > 0; + + # For barre, only the first and last finger. + if ( $bar->{$fing} && $fsh ne "below" ) { + next unless ( $sx == $bar->{$fing}->[2] + || $sx == $bar->{$fing}->[3] ); + } + + unless ( $did++ ) { + if ( $fsh eq "below" ) { + $size *= 1.4; + } + else { + $xo->fill_color($fbg); + } + $xo->textstart; + $xo->font( $font, $size ); + $asc = $font->ascender/1000 * $size; + } + if ( $fsh eq "below" ) { + $xo->translate( $x, -$nw - $lw - ($vc+1)*$gh ); + } + else { + $xo->translate( $x, -$nw - ($fret-0.5)*$gh - $dot/3 ); + } + $xo->text( $fing, align => "center" ); + } + $xo->textend if $did; + } + + return $xo; +} + +# The core grid. Just the horizontal and vertical lines, with a fitting +# bounding box. Origin is (half linewidth from) top left. +# Grid objects are cached globally. + +use constant DIAG_GRID_XO => 0; + +method grid_xo { + + return $dcache->{$gw,$gh,$lw,$nw,$fg,$vc,$hc} //= do { + + my $w = $gw * ($hc - 1); + my $h = $gh * $vc; + + my $xo = $pdf->xo_form; + + # Bounding box must take linewidth into account. + # Origin is top left, so y runs negative. + my $xp = DIAG_GRID_XO ? 2 : 0; + my @bb = ( -$lw/2 - $xp, $lw/2 + $xp, + $w + $lw/2 + $xp, -($vc * $gh + $nw) - $lw/2 - $xp ); + $xo->bbox(@bb); + $xo->line_width($lw); + + if ( DIAG_GRID_XO ) { + # Draw the grid. + $xo->fill_color('yellow'); + $xo->rectangle(@bb)->fill; + + # Draw additional nuts. + if ( 0&& $nutwidth > 1 ) { + for ( 0 .. $nutwidth-2 ) { + $xo->stroke_color( $_ % 2 ? "#c0c0c0" : "#e0e0e0" ); + $xo->move( -$lw/2, -$_*$lw ); + $xo->hline( $w + $lw/2 )->stroke; + } + } + } + + $xo->stroke_color($fg); + for ( 0 .. $vc ) { + $xo->move( -$lw/2, -$_*$gh - $nw); + $xo->hline( $w + $lw/2 ); + } + for ( 0 .. $hc-1 ) { + $xo->move( $_*$gw, $lw/2 ); + $xo->vline( -($gh*$vc + $nw + $lw/2 ) ); + } + $xo->stroke; + + if ( DIAG_GRID_XO ) { + # Show origin. + $xo->fill_color('red'); + my $lw = $lw/2; + $xo->rectangle( -$lw, -$lw, $lw, $lw )->fill; + } + + $xo; + }; +} + +1; diff --git a/lib/ChordPro/Output/PDF/StringDiagrams.pm b/lib/ChordPro/Output/PDF/StringDiagrams.pm deleted file mode 100644 index ecc15cf6..00000000 --- a/lib/ChordPro/Output/PDF/StringDiagrams.pm +++ /dev/null @@ -1,327 +0,0 @@ -#! perl - -use strict; - -package main; - -our $config; - -package ChordPro::Output::PDF::StringDiagrams; - -use ChordPro::Chords; - -sub new { - my ( $pkg, $ps ) = @_; - - my $ctl = $ps->{kbdiagrams}; - - my $show = $ctl->{show}; - unless ( $show =~ /^(?:top|bottom|right|below)$/i ) { - die("pdf.diagrams.show is \"$show\", must be one of ". - "\"top\", \"bottom\", \"right\", or \"below\"\n"); - } - - bless { ps => $ps } => $pkg; -} - -# The vertical space the diagram requires. -sub vsp0 { - my ( $self, $elt, $ps ) = @_; - $ps->{fonts}->{diagram}->{size} * $ps->{spacing}->{diagramchords} - + ( $ps->{diagrams}->{nutwidth} * ($ps->{diagrams}->{linewidth} || 0.10) + 0.40 ) - * $ps->{diagrams}->{width} - + $ps->{diagrams}->{vcells} * $ps->{diagrams}->{height} - + ( $ps->{diagrams}->{fingers} eq "below" ? $ps->{fonts}->{diagram}->{size} : 0 ) - ; -} - -# The advance height. -sub vsp1 { - my ( $self, $elt, $ps ) = @_; - $ps->{diagrams}->{vspace} * $ps->{diagrams}->{height}; -} - -# The vertical space the diagram requires, including advance height. -sub vsp { - my ( $self, $elt, $ps ) = @_; - $self->vsp0( $elt, $ps ) + $self->vsp1( $elt, $ps ); -} - -# The horizontal space the diagram requires. -sub hsp0 { - my ( $self, $elt, $ps ) = @_; - ($config->diagram_strings - 1) * $ps->{diagrams}->{width}; -} - -# The advance width. -sub hsp1 { - my ( $self, $elt, $ps ) = @_; - $ps->{diagrams}->{hspace} * $ps->{diagrams}->{width}; -} - -# The horizontal space the diagram requires, including advance width. -sub hsp { - my ( $self, $elt, $ps ) = @_; - $self->hsp0( $elt, $ps ) + $self->hsp1( $elt, $ps ); -} - -sub font_bl { - goto &ChordPro::Output::PDF::font_bl; -} - -# The actual draw method. -sub draw { - my ( $self, $info, $x, $y, $ps ) = @_; - return unless $info; - - my $x0 = $x; - my $y0 = $y; - - my $gw = $ps->{diagrams}->{width}; - my $gh = $ps->{diagrams}->{height}; - my $dot = $ps->{diagrams}->{dotsize} * $gw; - my $bsz = $ps->{diagrams}->{barwidth} * $dot; - my $lw = ($ps->{diagrams}->{linewidth} || 0.10) * $gw; - my $bflw = $ps->{diagrams}->{nutwidth} * $lw; - my $fsh = $ps->{diagrams}->{fingers}; # t / f / below - my $bfy = $bflw/3; - - my $pr = $ps->{pr}; - - my $fg = $info->{diagram} // $config->{pdf}->{theme}->{foreground}; - my $strings = $config->diagram_strings; - my $w = $gw * ($strings - 1); - - # Draw font name. - my $font = $ps->{fonts}->{diagram}; - my $fasc = $font->{fd}->{font}->ascender/1000; - $pr->setfont($font); - my $name = $info->chord_display; - # $name .= "*" - # unless $info->{origin} ne "user" - # || $::config->{diagrams}->{show} eq "user"; - $name = "$name" - if $info->{diagram}; - $pr->text( $name, $x + ($w - $pr->strwidth($name))/2, $y - font_bl($font) ); - $y -= $font->{size} * $ps->{spacing}->{diagramchords} + $dot/2 + $lw; - if ( $info->{base} + $info->{baselabeloffset} > 1 ) { - my $i = sprintf("%d ", $info->{base} + $info->{baselabeloffset}); - $pr->setfont( $ps->{fonts}->{diagram_base}, $gh ); - $pr->text( $i, $x-$pr->strwidth($i), - $y-$bfy - $bflw/2 - ($info->{baselabeloffset}*$gh)-0.85*$gh, - $ps->{fonts}->{diagram_base}, $ps->{spacing}->{diagramchords}*$gh ); - $pr->setfont($font); - } - - my $v = $ps->{diagrams}->{vcells}; - my $h = $strings; - - my $basefretno = $info->{base} + $info->{baselabeloffset}; - # Draw the grid. - my $xo = $self->grid_xo( $ps, $basefretno, $fg ); - - my $crosshairs = sub { - my ( $x, $y, $col ) = @_; - for ( $pr->{pdfgfx} ) { - $_->save; - $_->linewidth(0.1); - $_->strokecolor($col//"black"); - $_->move($x-10,$y); - $_->hline($x+20); - $_->stroke; - $_->move($x,$y+10); - $_->vline($y-20); - $_->stroke; - $_->restore; - } - }; - - $pr->{pdfgfx}->formimage( $xo, $x, $y-$bfy-$v*$gh, 1 ); - - # The numbercolor property of the chordfingers is used for the - # background of the underlying dot (the numbers are transparent). - my $fcf = $ps->{fonts}->{chordfingers}; - my $fbg = $pr->_bgcolor($fcf->{numbercolor}); - # However, if none we should really use white. - $fbg = "white" if $fbg eq "none"; - - my $fingers; - $fingers = $info->{fingers} if $fsh; - - # Bar detection. - my $bar; - if ( $fingers && $fbg ne $fg ) { - my %h; - my $str = 0; - my $got = 0; - foreach ( @{ $fingers } ) { - $str++, next unless $info->{frets}->[$str] > 0; - if ( $bar->{$_} ) { - # Same finger on multiple strings -> bar. - $got++; - $bar->{$_}->[-1] = $str; - } - else { - # Register. - $bar->{$_} = [ $_, $info->{frets}->[$str], $str, $str ]; - } - $str++; - } - if ( $got ) { - foreach (sort keys %$bar ) { - my @bi = @{ $bar->{$_} }; - if ( $bi[-2] == $bi[-1] ) { # not a bar - delete $bar->{$_}; - next; - } - # Print the bar line. Need linecap 0. - $pr->hline( $x+$bi[2]*$gw, $y-$bfy -$bflw/2 -$bi[1]*$gh+$gh/2, - ($bi[3]-$bi[2])*$gw, - $bsz, $fg, 0 ); - } - } - } - - # Process the strings and fingers. -# $x -= $gw/2; - my $oflo; # to detect out of range frets - - my $g_none = "/"; # unnumbered - - # All symbols from the chordfingers font are equal size: a circle - # of 824 (1000-2*88) centered horizontally in the box, with a - # decender of 55. - # To get it vertically centered we must lower it by 455 (1000/2-55). - $pr->setfont($fcf,$dot); - my $g_width = $pr->strwidth("1"); - $x -= 0.65 * $g_width; - my $g_lower = -0.455*$g_width; -# warn("GW dot=$dot, width=$g_width, lower=$g_lower\n"); -# my $e = $fcf->{fd}->{font}->extents("1",10); -# use DDumper; DDumper($e); - - for my $sx ( 0 .. @{ $info->{frets} // [] }-1 ) { - my $fret = $info->{frets}->[$sx]; - my $fing = -1; - $fing = $fingers->[$sx] // -1 if $fingers; - - # For bars, only the first and last finger. - if ( $fing && $bar && $bar->{$fing} ) { - unless ( $sx == $bar->{$fing}->[2] || $sx == $bar->{$fing}->[3] ) { - if ( $fsh eq "below" && $fing =~ /^[A-Z0-9]$/ ){ - $pr->setfont( $font, $dot ); - my $w = $pr->strwidth($fing); - $pr->text( $fing, - $x + 0.65*$g_width -$w/2, - $y - $bfy - $bflw/2-($v+0.3)*$gh - $dot*1.4*$fasc, - $font, $dot*1.4 ); - } - next; - } - } - - if ( $fret > 0 ) { - if ( $fret > $v && !$oflo++ ) { - warn("Diagram $info->{name}: ", - "Fret position $fret exceeds diagram size $v\n"); - } - - my $glyph; - if ( $fbg eq $fg || $fsh eq "below" ) { - $glyph = $g_none; - } - elsif ( $fing =~ /^[A-Z0-9]$/ ) { - # Leave it to the user to interpret sensibly. - $glyph = $fing; - } - elsif ( $fing =~ /-\d+$/ ) { - $glyph = $g_none; - } - else { - warn("Diagram $info->{name}: ", - "Invalid finger position $fing, ignored\n"); - $glyph = $g_none; - } - - # The glyphs are open, so we need am explicit - # background circle to prevent the grid peeping through. - $pr->circle( $x+$gw/2, $y-$bfy-$bflw/2-$fret*$gh+$gh/2, $dot/2.2, 1, - $fbg, "none") unless $glyph eq $g_none; - - $pr->setfont( $fcf, $dot ); - $glyph = "$glyph" - if $info->{diagram}; - $pr->text( $glyph, - $x, - $y - $bfy - $bflw/2-$fret*$gh + $gh/2 + $g_lower, - $fcf, $dot/0.8 ); - - if ( $fsh eq "below" && $fing =~ /^[A-Z0-9]$/ ){ - $pr->setfont( $font, $dot*1.4 ); - my $w = $pr->strwidth($fing); - $pr->text( $fing, - $x + 0.65*$g_width -$w/2, - $y - $bfy - $bflw/2-($v+0.3)*$gh - $dot*1.4*$fasc, - $font, $dot*1.4 ); - } - - } - elsif ( $fret < 0 ) { - $pr->cross( $x+$gw/2, $y+$lw+$gh/3, $dot/3, $lw, $fg ); - } - elsif ( $info->{base} > 0 ) { - $pr->circle( $x+$gw/2, $y+$lw+$gh/3, $dot/3, $lw, undef, $fg ); - } - } - continue { - $x += $gw; - } - - return $gw * ( $ps->{diagrams}->{hspace} + $strings ); -} - -sub grid_xo { - my ( $self, $ps, $basefretno, $fg ) = @_; - - my $gw = $ps->{diagrams}->{width}; - my $gh = $ps->{diagrams}->{height}; - my $lw = ($ps->{diagrams}->{linewidth} || 0.10) * $gw; - my $bflw = $ps->{diagrams}->{nutwidth} * $lw; - my $bfno = $basefretno; - my $v = $ps->{diagrams}->{vcells}; - my $strings = $config->diagram_strings; - - return $self->{grids}->{$gw,$gh,$lw, $bflw, $bfno, $fg, $v,$strings} //= do - { - my $w = $gw * ($strings - 1); - my $h = $strings; - - my $form = $ps->{pr}->{pdf}->xo_form; - - # Bounding box must take linewidth into account. - my @bb = ( -$lw/2, -$lw/2 - $bflw/2, - ($h-1)*$gw+$lw/2, $v*$gh+$lw/2 + $bflw/2.5 ); - $form->bbox(@bb); - - # Pseudo-object to access low level drawing routines. - my $dc = bless { pdfgfx => $form } => ChordPro::Output::PDF::Writer::; - - # Draw the grid. - $dc->rectxy( @bb, 0, 'red' ) if 0; - my $color = $fg; - for ( 0 .. $v ) { - if ( $bfno <= 1 && $_== 0 ) { - $dc->hline( 0, ($v-$_)*$gh, $w, $bflw, $color ); - } - else { - $dc->hline( 0, ($v-$_)*$gh-$bflw/2, $w, $lw, $color ); - } - } - - $dc->vline( $_*$gw, $v*$gh+$bflw/4, $gh*$v+$bflw/1.5, $lw, $color) for 0..$h-1; - - $form; - }; -} - -1; diff --git a/lib/ChordPro/Output/PDF/Writer.pm b/lib/ChordPro/Output/PDF/Writer.pm index 2d7ce623..657f6f91 100644 --- a/lib/ChordPro/Output/PDF/Writer.pm +++ b/lib/ChordPro/Output/PDF/Writer.pm @@ -14,8 +14,10 @@ use IO::String; use Carp; use utf8; -use ChordPro::Utils qw( expand_tilde demarkup ); +use ChordPro::Paths; +use ChordPro::Utils qw( expand_tilde demarkup min ); use ChordPro::Output::Common qw( fmt_subst prep_outlines ); +use File::LoadLines qw(loadlines); # For regression testing, run perl with PERL_HASH_SEED set to zero. # This eliminates the arbitrary order of font definitions and triggers @@ -36,6 +38,15 @@ sub new { $self->{layout} = Text::Layout->new( $self->{pdf} ); $self->{tmplayout} = undef; + # Patches and enhancements to PDF library. + no strict 'refs'; + *{$pdfapi . '::Resource::XObject::Form::width' } = \&_xo_width; + *{$pdfapi . '::Resource::XObject::Form::height'} = \&_xo_height; + if ( $pdfapi eq 'PDF::API2' ) { + no warnings 'redefine'; + *{$pdfapi . '::_is_date'} = sub { 1 }; + } + %fontcache = (); $self; @@ -46,13 +57,9 @@ sub info { $info{CreationDate} //= pdf_date(); - # PDF::API2 2.42+ does not accept the final apostrophe. - no warnings 'redefine'; - local *PDF::API2::_is_date = sub { 1 }; - if ( $self->{pdf}->can("info_metadata") ) { for ( keys(%info) ) { - $self->{pdf}->info_metadata( $_, $info{$_} ); + $self->{pdf}->info_metadata( $_, demarkup($info{$_}) ); } } else { @@ -161,6 +168,7 @@ sub text { else { $self->{layout}->set_markup($text); for ( @{ $self->{layout}->{_content} } ) { + next unless $_->{type} eq "text"; $_->{text} =~ s/\'/\x{2019}/g; # friendly quote } } @@ -182,7 +190,6 @@ sub text { # Draw background and.or frame. my $d = $debug ? 0 : 1; $frame = $debug || $font->{color} || $self->{ps}->{theme}->{foreground} if $frame; - # $self->crosshair( $x, $y, 20, 0.2, "magenta" ); $self->rectxy( $x + $e->{x} - $d, $y + $e->{y} + $d, $x + $e->{x} + $e->{width} + $d, @@ -309,8 +316,9 @@ sub circle { $gfx->fillcolor($self->_fgcolor($fillcolor)) if $fillcolor; $gfx->linewidth($lw||1); $gfx->circle( $x, $y, $r ); - $gfx->fill if $fillcolor; - $gfx->stroke if $strokecolor; + $gfx->fill if $fillcolor && !$strokecolor; + $gfx->fillstroke if $fillcolor && $strokecolor; + $gfx->stroke if $strokecolor && !$fillcolor; $gfx->restore; } @@ -330,68 +338,147 @@ sub cross { $gfx->restore; } -sub crosshair { # for debugging - my ( $self, $x, $y, $r, $lw, $strokecolor ) = @_; - my $gfx = $self->{pdfgfx}; - $gfx->save; - $gfx->strokecolor($self->_fgcolor($strokecolor)) if $strokecolor; - $gfx->linewidth($lw||1); - $gfx->move( $x, $y - $r ); - $gfx->line( $x, $y + $r ); - $gfx->stroke if $strokecolor; - $gfx->move( $x - $r, $y ); - $gfx->line( $x + $r, $y ); - $gfx->stroke if $strokecolor; - $gfx->restore; -} - +# Fetch an image or xform object. +# Source is $elt->{uri} (for files), $elt->{chord} (for chords). +# Result is delivered, and stored in $elt->{data}; sub get_image { my ( $self, $elt ) = @_; my $img; + my $subtype = $elt->{subtype}; + my $data; + + if ( $subtype eq "delegate" ) { + croak("delegated image in get_image()"); + } + + if ( $elt->{data} ) { # have data + $data = $elt->{data}; + warn("get_image($elt->{subtype}): data ", length($data), " bytes\n") + if $config->{debug}->{images}; + return $data; + } + my $uri = $elt->{uri}; - warn("get_image($uri)\n") if $config->{debug}->{images}; - if ( $uri =~ /^id=(.+)/ ) { - my $a = $ChordPro::Output::PDF::assets->{$1}; + if ( !$subtype && $uri =~ /\.(\w+)$/ ) { + $subtype //= $1; + } - if ( $a->{type} eq "abc" ) { - my $res = ChordPro::Output::PDF::abc2image( undef, $self, $a ); - return $self->get_image( { %$elt, uri => $res->{src} } ); - } - elsif ( $a->{type} eq "jpg" ) { - $img = $self->{pdf}->image_jpeg(IO::String->new($a->{data})); - } - elsif ( $a->{type} eq "png" ) { - $img = $self->{pdf}->image_png(IO::String->new($a->{data})); - } - elsif ( $a->{type} eq "gif" ) { - $img = $self->{pdf}->image_gif(IO::String->new($a->{data})); - } - return $img; + if ( $subtype =~ /^(jpg|png|gif)$/ ) { + $img = $self->{pdf}->image($uri); + warn("get_image($subtype, $uri): img ", length($img), " bytes\n") + if $config->{debug}->{images}; + } + elsif ( $subtype =~ /^(xform)$/ ) { + $img = $data; + warn("get_image($subtype): xobject (", +# join(" ", $img->bbox), + join(" ", @{$data->{bbox}}), + ")\n") + if $config->{debug}->{images}; } - for ( $uri ) { - $img = $self->{pdf}->image_png($_) if /\.png$/i; - $img = $self->{pdf}->image_jpeg($_) if /\.jpe?g$/i; - $img = $self->{pdf}->image_gif($_) if /\.gif$/i; + else { + croak("Unhandled image type: $subtype\n"); } return $img; } -sub add_image { - my ( $self, $img, $x, $y, $w, $h, $border ) = @_; +sub _xo_width { + my ( $self ) = @_; + my @bb = $self->bbox; + return abs($bb[2]-$bb[0]); +} +sub _xo_height { + my ( $self ) = @_; + my @bb = $self->bbox; + return abs($bb[3]-$bb[1]); +} + +sub add_object { + my ( $self, $o, $x, $y, %options ) = @_; + + my $scale_x = $options{"xscale"} || $options{"scale"} || 1; + my $scale_y = $options{"yscale"} || $options{"scale"} || $scale_x; + + my $va = $options{valign} // "bottom"; + my $ha = $options{align} // "left"; my $gfx = $self->{pdfgfx}; + my $w = $o->width * $scale_x; + my $h = $o->height * $scale_y; + + warn( sprintf("add_object x=%.1f y=%.1f w=%.1f h=%.1f scale=%.1f,%.1f) %s\n", + $x, $y, $w, $h, $scale_x, $scale_y, $ha, + ) ) if $config->{debug}->{images}; + + $self->crosshairs( $x, $y, color => "lime" ) if $config->{debug}->{images}; + if ( $va eq "top" ) { + $y -= $h; + } + elsif ( $va eq "middle" ) { + $y -= $h/2; + } + if ( $ha eq "right" ) { + $x -= $w; + } + elsif ( $ha eq "center" ) { + $x -= $w/2; + } + $self->crosshairs( $x, $y, color => "red" ) if $config->{debug}->{images}; $gfx->save; - $gfx->image( $img, $x, $y-$h, $w, $h ); - if ( $border ) { - $gfx->rect( $x, $y-$h, $w, $h ) - ->linewidth($border) + if ( ref($o) =~ /::Resource::XObject::Image::/ ) { + # Image wants width and height. + $gfx->object( $o, $x, $y, $w, $h ); + } + else { + # XO_Form wants xscale and yscale. + my @bb = $o->bbox; + $gfx->object( $o, $x-min($bb[0],$bb[2])*$scale_x, + $y-min($bb[1],$bb[3])*$scale_y, $scale_x, $scale_y ); + } + + if ( $options{border} ) { + my $bc = $options{"bordercolor"} || $options{"color"}; + $gfx->stroke_color($bc) if $bc; + $gfx->rectangle( $x, $y, $x+$w, $y+$h ) + ->line_width( $options{border} ) ->stroke; } + $gfx->restore; } +# For convenience. +sub crosshairs { + my ( $self, $x, $y, %options ) = @_; + my $gfx = $self->{pdfgfx}; + my $col = $options{colour} || $options{color} || "black"; + my $lw = $options{linewidth} || 0.1; + my $w = ( $options{width} || 40 ) / 2; + my $h = ( $options{width} || $options{height} || 40 ) / 2; + for ( $gfx ) { + $_->save; + $_->line_width($lw); + $_->stroke_color($col); + $_->move($x-$w,$y); + $_->hline($x+$w); + $_->move($x,$y+$h); + $_->vline($y-$h); + $_->stroke; + $_->restore; + } +} + +sub add_image { + my ( $self, $img, $x, $y, $w, $h, $border ) = @_; + $self->add_object( $img, $x, $y, + xscale => $w/$img->width, + yscale => $h/$img->height, + valign => "bottom", + $border ? ( border => $border ) : () ); +} + sub newpage { my ( $self, $ps, $page ) = @_; #$self->{pdftext}->textend if $self->{pdftext}; @@ -423,6 +510,7 @@ sub newpage { sub openpage { my ( $self, $ps, $page ) = @_; $self->{pdfpage} = $self->{pdf}->openpage($page); + confess("Fatal: Page $page not found.") unless $self->{pdfpage}; $self->{pdfgfx} = $self->{pdfpage}->gfx; $self->{pdftext} = $self->{pdfpage}->text; } @@ -540,6 +628,10 @@ sub make_outlines { } } else { + ####TODO: Why? + if ( @$book == 1 && ref($book->[0]) eq 'ChordPro::Song' ) { + $book = [[ $book->[0] ]]; + } foreach my $b ( @$book ) { my $song = $b->[-1]; # Leaf outline. @@ -578,7 +670,7 @@ sub init_fonts { my $fc = Text::Layout::FontConfig->new( debug => $config->{debug}->{fonts} > 1 ); # Add font dirs. - my @d = ( @{$ps->{fontdir}}, ::rsc_or_file("fonts/"), $ENV{FONTDIR} ); + my @d = ( @{$ps->{fontdir}}, @{ CP->findresdirs("fonts") }, $ENV{FONTDIR} ); # Avoid rsc result if dummy. splice( @d, -2, 1 ) if $d[-2] eq "fonts/"; for my $fontdir ( @d ) { diff --git a/lib/ChordPro/Paths.pm b/lib/ChordPro/Paths.pm new file mode 100644 index 00000000..36041b4d --- /dev/null +++ b/lib/ChordPro/Paths.pm @@ -0,0 +1,343 @@ +#! perl + +use v5.26; +use Object::Pad; +use utf8; +use Carp; + +class ChordPro::Paths; + +my $instance; + +method get :common ( $reset = 0 ) { + undef $instance if $reset; + $instance //= $class->new; +} + +use Cwd qw(realpath); +use File::Spec::Functions qw( catfile catdir splitpath catpath ); +use File::HomeDir; + +field $home :reader; # dir +field $configdir :reader; # dir +field $privlib :reader; # dir +field $resdirs :reader; # [ dir, ... ] +field $configs :reader; # { config => dir, ... } +field $pathsep :reader; # : or ; + +field $packager :reader; + +# Cwd::realpath always returns forward slashes. +# On Windows, Cwd::realpath always returns a volume. + +BUILD { + my $app = "ChordPro"; + my $app_lc = lc($app); + + $pathsep = $self->is_msw ? ';' : ':'; + + $home = realpath( $ENV{HOME} = File::HomeDir->my_home ); + +# $desktop = File::HomeDir->my_desktop; +# $docs = File::HomeDir->my_documents; +# $music = File::HomeDir->my_music; +# $pics = File::HomeDir->my_pictures; +# $videos = File::HomeDir->my_videos; +# $data = File::HomeDir->my_data; +# $dist = File::HomeDir->my_dist_data('ChordPro'); +# $dist = File::HomeDir->my_dist_config('ChordPro'); + + # Establish config files. Global config is easy. + for ( $self->normalize("/etc/$app_lc.json") ) { + next unless $_ && -f; + $configs->{sysconfig} = $_; + } + + $configs = {}; + # The user specific config requires some alternatives. + # -d $XDG_CONFIG_HOME/$app_lc + # -d ~/.config/$app_lc + # -d ~/.$app_lc + # -d my_dist_config + my @try; + if ( defined( $ENV{XDG_CONFIG_HOME} ) ) { + push( @try, + catdir( $ENV{XDG_CONFIG_HOME}, ".config", $app_lc ), + catdir( $ENV{XDG_CONFIG_HOME}, ".config" ), + catdir( $ENV{XDG_CONFIG_HOME}, ".$app_lc" ) ); + } + else { + push( @try, + catdir( $home, ".config", $app_lc ), + catdir( $home, ".$app_lc" ), + File::HomeDir->my_dist_config($app) ); + } + + for ( @try ) { + next unless $_ && -d $_; + my $path = $self->normalize($_); + warn("Paths: configdir try $_ => $path\n") if $self->debug > 1; + next unless $path && -d $path; + $configdir = $path; + for ( $self->normalize( catfile( $path, "$app_lc.prp" ) ), + $self->normalize( catfile( $path, "$app_lc.json" ) ) ) { + next unless $_ && -f $_; + $configs->{userconfig} = $_; + last; + } + last if $configdir; + } + warn("Paths: configdir = ", $configdir // "", "\n") if $self->debug; + + for ( $self->normalize(".$app_lc.json"), + $self->normalize("$app_lc.json") ) { + next unless $_ && -f $_; + $configs->{config} = $_; + last; + } + if ( $self->debug ) { + for ( qw( sysconfig userconfig config ) ) { + warn(sprintf("Paths: %-10s = %s\n", + $_, $configs->{$_} // "" ) ); + } + } + + # Private lib. + $privlib = $INC{'ChordPro.pm'} =~ s/\.pm$/\/lib/r; + + # Now for the resources. + $resdirs = []; + @try = (); + push( @try, $self->path($ENV{CHORDPRO_LIB}) ) + if defined($ENV{CHORDPRO_LIB}); + push( @try, $configdir ) if $configdir; + push( @try, $INC{'ChordPro.pm'} =~ s/\.pm$/\/res/r ); + + for ( @try ) { + next unless $_; + my $path = $self->normalize($_); + warn("Paths: resdirs try $_ => $path\n") if $self->debug > 1; + next unless $path && -d $path; + push( @$resdirs, $path ); + } + + if ( $self->debug ) { + for ( 0..$#{$resdirs} ) { + warn("Paths: resdirs[$_] = $resdirs->[$_]\n"); + } + } + + unless ( @$resdirs ) { + warn("Paths: Cannot find resources, prepare for disaster\n"); + } + + # Check for packaged image. + for ( qw( Docker AppImage PPL ) ) { + next unless exists $ENV{uc($_)."_PACKAGED"} + && $ENV{uc($_)."_PACKAGED"}; + $packager = $_; + last; + } + +}; + +method debug { + # We need to take an env var into account, since the Paths + # singleton is created far before any config processing. + $ENV{CHORDPRO_DEBUG_PATHS} || $::config->{debug}->{paths} || 0; +} + +method is_msw { + $^O =~ /mswin/i; +} + +# Is absolute. + +method is_absolute ( $p ) { + File::Spec->file_name_is_absolute( $p ); +} + +# Is bare (no volume/dir). + +method is_here ( $p ) { + my ( $v, $d, $f ) = splitpath($p); + $v eq '' && $d eq ''; +} + +# Normalize - full path, forward slashes, ~ expanded. + +method normalize ( $p, %opts ) { + $p = $home . "/$1" if $p =~ /~[\\\/](.*)/; + realpath($p) +} + +# This is only used in ::runtimeinfo for display purposes. + +method display ( $p ) { + return "" unless defined $p; + $p = $self->normalize($p); + if ( index( $p, $home ) == 0 ) { + substr( $p, 0, length($home), '~' ); + } + return $p; +} + +method path ( $p = undef ) { + if ( defined($p) ) { + local $ENV{PATH} = $p; + my @p = File::Spec->path(); + # On MSWindows, '.' is always prepended. + shift(@p) if $self->is_msw; + return @p; + } + return File::Spec->path(); +} + +# Prepend dirs in front of path. + +method pathprepend( @d ) { + $ENV{PATH} = join( $pathsep, @d, $ENV{PATH} ); +} + +method pathcombine( @d ) { + join( $pathsep, @d ); +} + +# Locate an executable file (program) using PATH. + +method findexe ( $p, %opts ) { + my $try = $p; + my $found; + if ( $self->is_msw ) { + $try .= ".exe"; + } + for ( $self->path ) { + my $e = catfile( $_, $try ); + $found = realpath($e), last if -f -x $e; + } + warn("Paths: findexe $p => ", $self->display($found), "\n") + if $self->debug; + return $found; +} + +# Locate a config file (prp or json) using respath. + +method findcfg ( $p ) { + my $found; + my @p; + if ( $p =~ /\.\w+$/ ) { + $found = realpath($p), last if -f -s $p; + @p = ( $p ); + } + else { + @p = ( "$p.prp", "$p.json" ); + } + unless ( $found ) { + for ( @$resdirs ) { + for my $cfg ( @p ) { + my $f = catfile( $_, "config", $cfg ); + $found = realpath($f), last if -f -s $f; + } + } + } + warn("Paths: findcfg $p => ", $self->display($found), "\n") + if $self->debug; + return $found; +} + +# Locate a resource file (optionally classified) using respath. + +method findres ( $p, %opts ) { + my $try = $p; + my $found; + if ( defined $opts{class} ) { + $try = catfile( $opts{class}, $try ); + } + for ( @$resdirs ) { + my $f = catfile( $_, $try ); + $found = realpath($f), last if -f -s $f; + } + warn("Paths: findres", $opts{class} ? " [$opts{class}]" : "", + " $p => ", $self->display($found), "\n") + if $self->debug; + return $found; +} + +# Locate resource directories (optionally classified) using respath. + +method findresdirs ( $p, %opts ) { + my $try = $p; + my @found; + if ( defined $opts{class} ) { + $p = catdir( $opts{class}, $p ); + } + for ( @$resdirs ) { + my $d = catdir( $_, $p ); + push( @found, realpath($d) ) if -d $d; + } + if ( $self->debug ) { + my $i = 0; + @found = ( "" ) unless @found; + warn("Paths: findresdirs[", + $opts{class} ? "$opts{class}:" : "", + $i++, "]", + " $p => ", $self->display($_), "\n") for @found; + } + return \@found; +} + +# Return the name of a sibling (i.e., same place, different name +# and/or extension). + +method sibling ( $orig, %opts ) { + # Split. + my ( $v, $d, $f ) = splitpath($orig); + my $res; + if ( $opts{name} ) { + $res = catpath( $v, $d, $opts{name} ); + } + else { + # Get base and extension. + my ( $b, $e ) = $f =~ /^(.*)(?:\.(\w+))$/; + # Adjust. + $b = $opts{base} if defined $opts{base}; + $e = $opts{ext} if defined $opts{ext}; + # New file name. + $f = $b; + $f .= $e if defined $e; + # Join with path. + $res = catpath( $v, $d, $f ); + } + warn("Paths: sibling $orig => ", $self->display($res), "\n") + if $self->debug; + return $res; +} + +# Given a file and a name, try name as a sibling, otherwise look it up. + +method siblingres ( $orig, $name, %opts ) { + return unless defined $orig; + my $try = $self->sibling( $orig, name => $name ); + my $found = ( $try && -s $try ) + ? $try + : $self->findres( $name, class => $opts{class} ); + return $found; +} + +method packager_version { + return unless $packager; + $ENV{uc($packager)."_PACKAGED"}; +} + +################ Export ################ + +# For convenience. + +use Exporter 'import'; +our @EXPORT; + +sub CP() { __PACKAGE__->get } + +push( @EXPORT, 'CP' ); + +1; diff --git a/lib/ChordPro/Song.pm b/lib/ChordPro/Song.pm index b6d39441..12c8bf50 100644 --- a/lib/ChordPro/Song.pm +++ b/lib/ChordPro/Song.pm @@ -1,5 +1,7 @@ #!/usr/bin/perl +use utf8; + package main; our $options; @@ -11,6 +13,7 @@ use strict; use warnings; use ChordPro; +use ChordPro::Paths; use ChordPro::Chords; use ChordPro::Chords::Appearance; use ChordPro::Chords::Parser; @@ -23,7 +26,6 @@ use File::LoadLines; use Storable qw(dclone); use feature 'state'; use Text::ParseWords qw(quotewords); -use File::Basename qw(basename); # Parser context. my $def_context = ""; @@ -68,12 +70,16 @@ my $no_substitute; my %propstack; my $diag; # for diagnostics +my @diag; # keep track of includes my $lineinfo; # keep lineinfo +my $assetid = "001"; # for assets # Constructor. sub new { - my ( $pkg, $filesource ) = @_; + my ( $pkg, $opts ) = @_; + + my $filesource = $opts->{filesource} || $opts->{_filesource}; $xpose = 0; $grid_arg = [ 4, 4, 1, 1 ]; # 1+4x4+1 @@ -96,6 +102,7 @@ sub new { bless { chordsinfo => {}, meta => {}, + generate => $opts->{generate}, structure => "linear", } => $pkg; } @@ -245,7 +252,7 @@ sub parse_song { my $target = $config->{settings}->{transcode}; if ( $target ) { unless ( ChordPro::Chords::Parser->have_parser($target) ) { - if ( my $file = ::rsc_or_file("config/notes/$target.json") ) { + if ( my $file = CP->findres("config/notes/$target.json") ) { for ( ChordPro::Config::get_config($file) ) { my $new = $config->hmerge($_); local $config = $new; @@ -351,8 +358,17 @@ sub parse_song { next; } - if ( /^\s*\{(new_song|ns)\}\s*$/ ) { - last if $self->{body}; + if ( /^\s*\{((?:new_song|ns)\b.*)\}\s*$/ ) { + if ( $self->{body} ) { + unshift( @$lines, $_ ); + $$linecnt--; + last; + } + my $dir = $self->parse_directive($1); + my $kv = parse_kv($dir->{arg}//""); + if ( $kv && $kv->{toc} ) { + $self->{meta}->{_TOC} = [ $kv->{toc} ]; + } next; } @@ -361,9 +377,10 @@ sub parse_song { # Handle assets. my $kw = ""; my $kv = {}; - if ( /^##(image|asset):\s+(.*)/i ) { + if ( /^##(image|asset|include)(?:-(.+))?:\s+(.*)/i + && $self->selected($2) ) { $kw = lc($1); - $kv = parse_kv($2); + $kv = parse_kv($3); } if ( $kw eq "image" ) { @@ -394,16 +411,20 @@ sub parse_song { # Store in assets. $self->{assets} //= {}; $self->{assets}->{$id} = - { data => $data, type => $info->{file_ext}, - width => $info->{width}, height => $info->{height}, - $kv->{persist} ? ( persist => 1 ) : (), + { type => "image", + data => $data, + subtype => $info->{file_ext}, + width => $info->{width}, + height => $info->{height}, + opts => $kv, }; if ( $config->{debug}->{images} ) { - warn("asset[$id] ", length($data), " bytes, ", - "width=$info->{width}, height=$info->{height}", - $kv->{persist} ? ", persist" : "", - "\n"); + warn( "asset[$id] type=image/$info->{file_ext} ", + length($data), " bytes, ", + "width=$info->{width}, height=$info->{height}", + $kv->{persist} ? ", persist" : "", + "\n"); } next; } @@ -419,6 +440,10 @@ sub parse_song { do_warn("Missing type for asset\n"); next; } + unless ( exists $config->{delegates}->{$type} ) { + do_warn("Unhandled type for asset: $type\n"); + next; + } # Read the data. my @data; @@ -430,17 +455,57 @@ sub parse_song { # Store in assets. $self->{assets} //= {}; $self->{assets}->{$id} = - { data => \@data, type => $type, - subtype => $config->{delegates}->{$type}->{type}, + { data => \@data, + type => "image", + subtype => $type, + module => $config->{delegates}->{$type}->{module}, handler => $config->{delegates}->{$type}->{handler}, + opts => $kv, }; if ( $config->{debug}->{images} ) { - warn("asset[$id] ", ::dump($self->{assets}->{$id})); + warn("asset[$id] type=image/$type ", + scalar(@data), " lines", + $kv->{persist} ? ", persist" : "", + "\n"); + } + next; + } + + if ( $kw eq "include" ) { + if ( $kv->{end} ) { + $diag = pop( @diag ); + $$linecnt = $diag->{line}; + } + else { + my $uri = $kv->{src}; + if ( $uri && CP->is_here($uri) ) { + my $found = CP->siblingres( $diag->{file}, $uri, class => "include" ); + if ( $found ) { + $uri = $found; + } + else { + do_warn("Missing include for \"$uri\""); + $uri = undef; + } + } + if ( $uri ) { + unshift( @$lines, loadlines($uri), "##include: end=1" ); + push( @diag, { %$diag } ); + $diag->{file} = $uri; + $diag->{line} = $$linecnt = 0; + $diag->{orig} = "(including $uri)"; + } } next; } + # Currently the ChordPro backend is the only one that + # cares about comment lines. # Collect pre-title stuff separately. + next unless exists $config->{lc $self->{generate}} + && exists $config->{lc $self->{generate}}->{comments} + && $config->{lc $self->{generate}}->{comments} eq "retain"; + if ( exists $self->{title} || $fragment ) { $self->add( type => "ignore", text => $_ ); } @@ -450,6 +515,7 @@ sub parse_song { next; } + # Tab content goes literally. if ( $in_context eq "tab" ) { unless ( /^\s*\{(?:end_of_tab|eot)\}\s*$/ ) { $self->add( type => "tabline", text => $_ ); @@ -464,7 +530,64 @@ sub parse_song { } else { delete $self->{body}->[-1]->{open}; - # A subsequent {start_of_XXX} will reopen a new item + # A subsequent {start_of_XXX} will open a new item + + my $d = $config->{delegates}->{$in_context}; + if ( $d->{type} eq "image" ) { + local $_; + my $a = pop( @{ $self->{body} } ); + delete( $a->{context} ); + my $id = $a->{id}; + my $opts = {}; + unless ( $id ) { + my $pkg = 'ChordPro::Delegate::' . $a->{delegate}; + eval "require $pkg" || warn($@); + if ( my $c = $pkg->can("options") ) { + $opts = $c->($a->{data}); + $id = $opts->{id}; + } + } + $opts = $a->{opts} = { %$opts, %{$a->{opts}} }; + + my $def = !!$id; + $id //= "_Image".$assetid++; + + if ( defined $opts->{spread} ) { + $def++; + if ( exists $self->{spreadimage} ) { + do_warn("Skipping superfluous spread image"); + } + else { + $self->{spreadimage} = + { id => $id, space => $opts->{spread} }; + warn("Got spread image $id with space=$opts->{spread}\n") + if $config->{debug}->{images}; + } + } + + # Move to assets. + $self->{assets}->{$id} = $a; + if ( $def ) { + my $label = delete $a->{label}; + do_warn("Label \"$label\" ignored on non-displaying $in_context section\n") + if $label; + } + else { + my $label = delete $opts->{label}; + $self->add( type => "set", + name => "label", + value => $label ) + if $label && $label ne ""; + $self->add( type => "image", + opts => $opts, + id => $id ); + if ( $opts->{label} ) { + push( @labels, $opts->{label} ) + unless $in_context eq "chorus" + && !$config->{settings}->{choruslabels}; + } + } + } } } elsif ( $config->{delegates}->{$in_context}->{omit} ) { @@ -475,25 +598,13 @@ sub parse_song { if ( $self->{body} && @{ $self->{body} } && $self->{body}->[-1]->{context} eq $in_context && $self->{body}->[-1]->{open} ) { - push( @{$self->{body}->[-1]->{data}}, $_ ); + push( @{$self->{body}->[-1]->{data}}, + fmt_subst( $self, $_ ) ); } # Else start new item. else { - my %opts; - ####TODO - if ( $xpose || $config->{settings}->{transpose} ) { - $opts{transpose} = - $xpose + ($config->{settings}->{transpose}//0 ); - } - my $d = $config->{delegates}->{$in_context}; - $self->add( type => "delegate", - delegate => $d->{module}, - subtype => $d->{type}, - handler => $d->{handler}, - data => [ $_ ], - opts => \%opts, - open => 1 ); + croak("Reopening delegate"); } next; } @@ -537,7 +648,22 @@ sub parse_song { $prep->{songline}->($_); # warn("POST: ", $_, "\n"); } - $self->add( type => "songline", $self->decompose($_) ); + if ( $config->{settings}->{flowtext} + && @{ $self->{body}//[] } ) { + my $prev = $self->{body}->[-1]; + my $this = { $self->decompose($_) }; + if ( $prev->{type} eq "songline" + && !$prev->{chords} + && !$this->{chords} ) { + $prev->{phrases}->[0] .= " " . $this->{phrases}->[0]; + } + else { + $self->add( type => "songline", %$this ); + } + } + else { + $self->add( type => "songline", $self->decompose($_) ); + } } elsif ( exists $self->{title} || $fragment ) { $self->add( type => "empty" ); @@ -556,6 +682,8 @@ sub parse_song { warn("Processed song...\n") if $options->{verbose}; $diag->{format} = "\"%f\": %m"; + ::dump($self->{assets}, as => "Assets, Pass 1") + if $config->{debug}->{assets} & 1; $self->dump(0) if $config->{debug}->{song} > 1; if ( @labels ) { @@ -573,7 +701,7 @@ sub parse_song { if ( $xc ) { $info = $info->transcode($xc); } - $suppress{$info->name} = 1; + $suppress{$info->name} = $info->{origin} ne "song"; } # Suppress chords that the user don't want. while ( my ($k,$v) = each %{ $self->{chordsinfo} } ) { @@ -646,7 +774,8 @@ sub parse_song { } } - $self->dump(0) if $config->{debug}->{song}; + $self->dump(0) if $config->{debug}->{song} > 0; + $self->dump(2) if $config->{debug}->{song} < 0; $self->dump(1) if $config->{debug}->{songfull}; return $self; @@ -784,9 +913,9 @@ sub decompose { $memorizing++; } if ( $memorizing ) { - push( @$memchords, $chords[-1] ); + push( @$memchords, $chord eq "" ? "" : $chord ); warn("Chord memorized for $in_context\[$memcrdinx]: ", - $chords[-1], "\n") + $memchords->[-1], "\n") if $config->{debug}->{chords}; } $memcrdinx++; @@ -805,7 +934,7 @@ sub decompose { push( @chords, $chord ); } else { - push( @chords, $self->chord($memchords->[$memcrdinx]->chord_display)); + push( @chords, $self->chord($memchords->[$memcrdinx]) ); warn("Chord recall $in_context\[$memcrdinx]: ", $chords[-1], "\n") if $config->{debug}->{chords}; } @@ -942,56 +1071,40 @@ sub decompose_grid { ################ Parsing directives ################ -my @directives = qw( - chord - chordcolour - chordfont - chordsize - chorus - column_break - columns - comment - comment_box - comment_italic - define - end_of_bridge - end_of_chorus - end_of_grid - end_of_tab - end_of_verse - footersize - footercolour - footerfont - grid - highlight - image - meta - new_page - new_physical_page - new_song - no_grid - pagetype - start_of_bridge - start_of_chorus - start_of_grid - start_of_tab - start_of_verse - subtitle - tabcolour - tabfont - tabsize - textcolour - textfont - textsize - title - titlesize - titlecolour - titlefont - titles - tocsize - toccolour - tocfont - transpose +my %directives = ( + chord => \&define_chord, + chorus => \&dir_chorus, + column_break => \&dir_column_break, + columns => \&dir_columns, + comment => \&dir_comment, + comment_box => \&dir_comment, + comment_italic => \&dir_comment, + define => \&define_chord, + diagrams => \&dir_diagrams, + end_of_bridge => undef, + end_of_chorus => undef, + end_of_grid => undef, + end_of_tab => undef, + end_of_verse => undef, + grid => \&dir_grid, + highlight => \&dir_comment, + image => \&dir_image, + meta => \&dir_meta, + new_page => \&dir_new_page, + new_physical_page => \&dir_new_page, + new_song => \&dir_new_song, + no_grid => \&dir_no_grid, + pagesize => \&dir_papersize, + pagetype => \&dir_papersize, + start_of_bridge => undef, + start_of_chorus => undef, + start_of_grid => undef, + start_of_tab => undef, + start_of_verse => undef, + subtitle => \&dir_subtitle, + title => \&dir_title, + titles => \&dir_titles, + transpose => \&dir_transpose, ); # NOTE: Flex: start_of_... end_of_... x_... @@ -1002,13 +1115,11 @@ my %abbrevs = ( ci => "comment_italic", colb => "column_break", cs => "chordsize", - grid => "diagrams", # not really an abbrev eob => "end_of_bridge", eoc => "end_of_chorus", eot => "end_of_tab", eov => "end_of_verse", g => "diagrams", - highlight => "comment", # not really an abbrev ng => "no_grid", np => "new_page", npp => "new_physical_page", @@ -1032,11 +1143,13 @@ sub parse_directive { unless ( $dirpat ) { $dirpat = '(?:' . - join( '|', @directives, + join( '|', keys(%directives), @{$config->{metadata}->{keys}}, keys(%abbrevs), - '(?:start|end)_of_\w+' ) . - ')'; + '(?:start|end)_of_\w+', + '(?:(?:text|chord|chorus|tab|grid|diagrams|title|footer|toc)'. + '(?:font|size|colou?r))', + ) . ')'; $dirpat = qr/$dirpat/; } @@ -1055,16 +1168,7 @@ sub parse_directive { # Check for xxx-yyy selectors. if ( $dir =~ /^($dirpat)-(.+)$/ ) { $dir = $abbrevs{$1} // $1; - my $sel = $2; - my $negate = $sel =~ s/\!$//; - $sel = ( $sel eq lc($config->{instrument}->{type}) ) - || - ( $sel eq lc($config->{user}->{name}) - || - ( $self->{meta}->{lc $sel} && is_true($self->{meta}->{lc $sel}->[0]) ) - ); - $sel = !$sel if $negate; - unless ( $sel ) { + unless ( $self->selected($2) ) { if ( $dir =~ /^start_of_/ ) { return { name => $dir, arg => $arg, omit => 2 }; } @@ -1080,6 +1184,21 @@ sub parse_directive { return { name => $dir, arg => $arg, omit => 0 } } +# Process a selector. +sub selected { + my ( $self, $sel ) = @_; + return 1 unless defined $sel; + my $negate = $sel =~ s/\!$//; + $sel = ( $sel eq lc($config->{instrument}->{type}) ) + || + ( $sel eq lc($config->{user}->{name}) + || + ( $self->{meta}->{lc $sel} && is_true($self->{meta}->{lc $sel}->[0]) ) + ); + $sel = !$sel if $negate; + return $sel; +} + sub directive { my ( $self, $d ) = @_; @@ -1093,6 +1212,10 @@ sub directive { } my $dir = $dd->{name}; + if ( $directives{$dir} ) { + return $directives{$dir}->( $self, $dir, $arg, $dd->{arg} ); + } + # Context flags. if ( $dir =~ /^start_of_(\w+)$/ ) { @@ -1134,12 +1257,37 @@ sub directive { $grid_cells = [ $grid_arg->[0] * $grid_arg->[1], $grid_arg->[2], $grid_arg->[3] ]; } - elsif ( $arg && $arg ne "" ) { + elsif ( exists $config->{delegates}->{$in_context} ) { + my $d = $config->{delegates}->{$in_context}; + my $label = $arg; + my %opts; + if ( $xpose || $config->{settings}->{transpose} ) { + $opts{transpose} = + $xpose + ($config->{settings}->{transpose}//0 ); + } + my $kv = {}; + if ( $arg =~ /\b(id|label|scale|split|align|center)=(.+)/ ) { + $kv = parse_kv($arg); + } + else { + $kv->{label} = $arg if $arg ne ""; + } + $self->add( type => "image", + subtype => "delegate", + delegate => $d->{module}, + handler => $d->{handler}, + data => [ ], + opts => { %opts, %$kv }, + exists($kv->{id}) ? ( id => $kv->{id} ) : (), + open => 1 ); + } + elsif ( $arg ne "" ) { $self->add( type => "set", name => "label", value => $arg ); push( @labels, $arg ) - unless $in_context eq "chorus" && !$config->{settings}->{choruslabels}; + unless $in_context eq "chorus" + && !$config->{settings}->{choruslabels}; } else { do_warn("Garbage in start_of_$1: $arg (ignored)\n") @@ -1155,6 +1303,7 @@ sub directive { } return 1; } + if ( $dir =~ /^end_of_(\w+)$/ ) { do_warn("Not in " . ucfirst($1) . " context\n") unless $in_context eq $1; @@ -1165,499 +1314,587 @@ sub directive { undef $memchords; return 1; } - if ( $dir =~ /^chorus$/i ) { - if ( $in_context ) { - do_warn("{chorus} encountered while in $in_context context -- ignored\n"); - return 1; - } - - # Clone the chorus so we can modify the label, if required. - my $chorus = @chorus ? dclone(\@chorus) : []; - - if ( @$chorus && $arg && $arg ne "" ) { - if ( $chorus->[0]->{type} eq "set" && $chorus->[0]->{name} eq "label" ) { - $chorus->[0]->{value} = $arg; - } - else { - unshift( @$chorus, - { type => "set", - name => "label", - value => $arg, - context => "chorus", - } ); - } - push( @labels, $arg ) - if $config->{settings}->{choruslabels}; - } - - if ( $chorus_xpose != ( my $xp = $xpose ) ) { - $xp -= $chorus_xpose; - for ( @$chorus ) { - if ( $_->{type} eq "songline" ) { - for ( @{ $_->{chords} } ) { - next if $_ eq ''; - my $info = $self->{chordsinfo}->{$_->key}; - next if $info->is_annotation; - $info = $info->transpose($xp, $xpose <=> 0) if $xp; - $info = $info->new($info); - $_ = ChordPro::Chords::Appearance->new - ( key => $self->add_chord($info), - info => $info, - maybe format => $_->format - ); - } - } - } - } - $self->add( type => "rechorus", - @$chorus - ? ( "chorus" => $chorus ) - : (), - ); - return 1; + # Metadata extensions (legacy). Should use meta instead. + # Only accept the list from config. + if ( any { $_ eq $dir } @{ $config->{metadata}->{keys} } ) { + return $self->dir_meta( "meta", "$dir $arg" ); } - # Song settings. + # Formatting. {chordsize XX} and such. + if ( $dir =~ m/ ^( text | chord | chorus | tab | grid | diagrams + | title | footer | toc ) + ( font | size | colou?r ) + $/x ) { + my $item = $1; + my $prop = $2; - # Breaks. + $self->propset( $item, $prop, $arg ); - if ( $dir eq "column_break" ) { - $self->add( type => "colb" ); - return 1; - } + # Derived props. + $self->propset( "chorus", $prop, $arg ) if $item eq "text"; - if ( $dir eq "new_page" || $dir eq "new_physical_page" ) { - $self->add( type => "newpage" ); + #::dump( { %propstack, line => $diag->{line} } ); return 1; } - - if ( $dir eq "new_song" ) { - die("FATAL - cannot start a new song now\n"); + # More private hacks. + if ( !$options->{reference} && $d =~ /^([-+])([-\w.]+)$/i ) { + if ( $2 eq "dumpmeta" ) { + warn(::dump($self->{meta})); + } + $self->add( type => "set", + name => $2, + value => $1 eq "+" ? 1 : 0, + ); + return 1; } - # Comments. Strictly speaking they do not belong here. + if ( !$options->{reference} && $dir =~ /^\+([-\w.]+(?:\.[<>])?)$/ ) { + $self->add( type => "set", + name => $1, + value => $arg, + ); - if ( $dir =~ /^comment(_italic|_box)?$/ ) { - my %res = $self->cdecompose($arg); - $res{orig} = $dd->{arg}; - $self->add( type => $dir, %res ) - unless exists($res{text}) && $res{text} =~ /^[ \t]*$/; - return 1; - } + # THIS IS BASICALLY A COPY OF THE CODE IN Config.pm. + # TODO: GENERALIZE. + my $ccfg = {}; + my @k = split( /[:.]/, $1 ); + my $c = \$ccfg; # new + my $o = $config; # current + my $lk = pop(@k); # last key - # Images. - if ( $dir eq "image" ) { - my $res = parse_kv($arg); - my $uri; - my $id; - my %opts; - while ( my($k,$v) = each(%$res) ) { - if ( $k =~ /^(title)$/i && $v ne "" ) { - $opts{lc($k)} = $v; - } - elsif ( $k =~ /^(border|spread|center)$/i && $v =~ /^(\d+)$/ ) { - $opts{lc($k)} = $v; - } - elsif ( $k =~ /^(width|height)$/i && $v =~ /^(\d+(?:\.\d+)?\%?)$/ ) { - $opts{lc($k)} = $v; - } - elsif ( $k =~ /^(x|y)$/i && $v =~ /^([-+]?\d+(?:\.\d+)?\%?)$/ ) { - $opts{lc($k)} = $v; - } - elsif ( $k =~ /^(scale)$/ && $v =~ /^(\d+(?:\.\d+)?)(%)?$/ ) { - $opts{lc($k)} = $2 ? $1/100 : $1; - } - elsif ( $k =~ /^(center|border|spread)$/i ) { - $opts{lc($k)} = $v; - } - elsif ( $k =~ /^(src|uri)$/i && $v ne "" ) { - $uri = $v; - } - elsif ( $k =~ /^(id)$/i && $v ne "" ) { - $id = $v; - } - elsif ( $k =~ /^(anchor)$/i - && $v =~ /^(paper|page|column|float|line)$/ ) { - $opts{lc($k)} = lc($v); - } - elsif ( $uri ) { - do_warn( "Unknown image attribute: $k\n" ); - next; - } - # Assume just an image file uri. - else { - $uri = $k; - } + # Step through the keys. + foreach ( @k ) { + $c = \($$c->{$_}); + $o = $o->{$_}; } - # If the image name does not have a directory, look it up - # next to the song, and then in the images folder of the - # CHORDPRO_LIB. - if ( $uri && $uri !~ m;^([a-z]:)?[/\\];i ) { # not abs - use File::Basename qw(dirname); - L: for ( dirname($diag->{file}) ) { - $uri = "$_/$uri", last if -s "$_/$uri"; - for ( ::rsc_or_file("images/$uri") ) { - last unless $_; - $uri = $_, last L if -s $_; - } - do_warn("Missing image for \"$uri\""); - } + # Turn hash.array into hash.array.> (append). + if ( ref($o) eq 'HASH' && ref($o->{$lk}) eq 'ARRAY' ) { + $c = \($$c->{$lk}); + $o = $o->{$lk}; + $lk = '>'; } - # uri + id -> define asset - if ( $uri && $id ) { - # Define a new asset. - if ( %opts ) { - do_warn("Asset definition \"$id\" does not take attributes"); - return; + # Final key. Merge array if so. + if ( ( $lk =~ /^\d+$/ || $lk eq '>' || $lk eq '<' ) + && ref($o) eq 'ARRAY' ) { + unless ( ref($$c) eq 'ARRAY' ) { + # Only copy orig values the first time. + $$c->[$_] = $o->[$_] for 0..scalar(@{$o})-1; } - use Image::Info; - open( my $fd, '<:raw', $uri ); - unless ( $fd ) { - do_warn("$uri: $!"); - return; + if ( $lk eq '>' ) { + push( @{$$c}, $arg ); } - my $data = do { local $/; <$fd> }; - # Get info. - my $info = Image::Info::image_info(\$data); - if ( $info->{error} ) { - do_warn($info->{error}); - return; + elsif ( $lk eq '<' ) { + unshift( @{$$c}, $arg ); } - - # Store in assets. - $self->{assets} //= {}; - $self->{assets}->{$id} = - { data => $data, type => $info->{file_ext}, - width => $info->{width}, height => $info->{height}, - }; - - if ( $config->{debug}->{images} ) { - warn("asset[$id] ", length($data), " bytes, ", - "width=$info->{width}, height=$info->{height}", - "\n"); + else { + $$c->[$lk] = $arg; } - return 1; } - - $uri = "id=$id" if $id; - unless ( $uri ) { - do_warn( "Missing image source\n" ); - return; + else { + $$c->{$lk} = $arg; } - $self->add( type => $uri =~ /\.svg$/ ? "svg" : "image", - uri => $uri, - opts => \%opts ); - return 1; - } - if ( $dir eq "title" ) { - $self->{title} = $arg; - push( @{ $self->{meta}->{title} }, $arg ); + $config->augment($ccfg); + upd_config(); + return 1; } - if ( $dir eq "subtitle" ) { - push( @{ $self->{subtitle} }, $arg ); - push( @{ $self->{meta}->{subtitle} }, $arg ); + # Warn about unknowns, unless they are x_... form. + do_warn("Unknown directive: $d\n") + if $config->{settings}->{strict} && $d !~ /^x_/; + return; +} + +sub dir_chorus { + my ( $self, $dir, $arg ) = @_; + + if ( $in_context ) { + do_warn("{chorus} encountered while in $in_context context -- ignored\n"); return 1; } - # Metadata extensions (legacy). Should use meta instead. - # Only accept the list from config. - if ( any { $_ eq $dir } @{ $config->{metadata}->{keys} } ) { - $arg = "$dir $arg"; - $dir = "meta"; - } + # Clone the chorus so we can modify the label, if required. + my $chorus = @chorus ? dclone(\@chorus) : []; - # Metadata. - if ( $dir eq "meta" ) { - if ( $arg =~ /([^ :]+)[ :]+(.*)/ ) { - my $key = lc $1; - my @vals = ( $2 ); - if ( $config->{metadata}->{autosplit} ) { - @vals = map { s/s\+$//; $_ } - split( quotemeta($config->{metadata}->{separator}), $vals[0] ); + if ( @$chorus && $arg && $arg ne "" ) { + if ( $chorus->[0]->{type} eq "set" && $chorus->[0]->{name} eq "label" ) { + $chorus->[0]->{value} = $arg; + } + else { + unshift( @$chorus, + { type => "set", + name => "label", + value => $arg, + context => "chorus", + } ); + } + push( @labels, $arg ) + if $config->{settings}->{choruslabels}; + } + + if ( $chorus_xpose != ( my $xp = $xpose ) ) { + $xp -= $chorus_xpose; + for ( @$chorus ) { + if ( $_->{type} eq "songline" ) { + for ( @{ $_->{chords} } ) { + next if $_ eq ''; + my $info = $self->{chordsinfo}->{$_->key}; + next if $info->is_annotation; + $info = $info->transpose($xp, $xpose <=> 0) if $xp; + $info = $info->new($info); + $_ = ChordPro::Chords::Appearance->new + ( key => $self->add_chord($info), + info => $info, + maybe format => $_->format + ); + } } - my $m = $self->{meta}; + } + } - # User and instrument cannot be set here. - if ( $key eq "user" || $key eq "instrument" ) { - do_warn("\"$key\" can be set from config only.\n"); - return 1; - } + $self->add( type => "rechorus", + @$chorus + ? ( "chorus" => $chorus ) + : (), + ); + return 1; +} - for my $val ( @vals ) { +#### Directive handlers #### - if ( $key eq "key" ) { - $val =~ s/[\[\]]//g; - my $info = $self->parse_chord($val); - my $name = $info->name; - my $act = $name; +# Song settings. - if ( $capo ) { - $act = $self->add_chord( $info->transpose($capo) ); - $name = $act if $decapo; - } +# Breaks. - push( @{ $m->{key} }, $name ); - $m->{key_actual} = [ $act ]; -# warn("XX key=$name act=$act capo=", -# $capo//""," decapo=$decapo\n"); - return 1; - } +sub dir_column_break { + my ( $self, $dir, $arg ) = @_; + $self->add( type => "colb" ); + return 1; +} +sub dir_new_page { + my ( $self, $dir, $arg ) = @_; + $self->add( type => "newpage" ); + return 1; +} - if ( $key eq "capo" ) { - do_warn("Multiple capo settings may yield surprising results.") - if exists $m->{capo}; - - $capo = $val || undef; - if ( $capo && $m->{key} ) { - if ( $decapo ) { - my $key = $self->store_chord - ($self->{chordsinfo}->{$m->{key}->[-1]} - ->transpose($val)); - $m->{key}->[-1] = $key; - $key = $self->store_chord - ($self->{chordsinfo}->{$m->{key}->[-1]} - ->transpose($xpose)); - $m->{key_actual} = [ $key ]; - } - else { - my $act = $m->{key_actual}->[-1]; - $m->{key_from} = [ $act ]; - my $key = $self->store_chord - ($self->{chordsinfo}->{$act}->transpose($val)); - $m->{key_actual} = [ $key ]; - } - } - } +sub dir_new_song { + my ( $self, $dir, $arg ) = @_; + die("FATAL - cannot start a new song now\n"); +} - elsif ( $key eq "duration" && $val ) { - $val = duration($val); - } +# Comments. Strictly speaking they do not belong here. - if ( $config->{metadata}->{strict} - && ! any { $_ eq $key } @{ $config->{metadata}->{keys} } ) { - # Unknown, and strict. - do_warn("Unknown metadata item: $key") - if $config->{settings}->{strict}; - return; - } +sub dir_comment { + my ( $self, $dir, $arg, $orig ) = @_; + $dir = "comment" if $dir eq "highlight"; + my %res = $self->cdecompose($arg); + $res{orig} = $orig; + $self->add( type => $dir, %res ) + unless exists($res{text}) && $res{text} =~ /^[ \t]*$/; + return 1; +} - push( @{ $self->{meta}->{$key} }, $val ) if defined $val; +sub dir_image { + my ( $self, $dir, $arg ) = @_; + return 1 if $::running_under_test && !$arg; + my $res = parse_kv($arg); + my $uri; + my $id; + my $chord; + my $type; + my %opts; + while ( my($k,$v) = each(%$res) ) { + if ( $k =~ /^(title)$/i && $v ne "" ) { + $opts{lc($k)} = $v; + } + elsif ( $k =~ /^(border|spread|center|persist)$/i + && $v =~ /^(\d+)$/ ) { + if ( $k eq "center" && $v ) { + $opts{align} = $k; + } + else { + $opts{lc($k)} = $v; + } + } + elsif ( $k =~ /^(width|height)$/i + && $v =~ /^(\d+(?:\.\d+)?\%?)$/ ) { + $opts{lc($k)} = $v; + } + elsif ( $k =~ /^(x|y)$/i + && $v =~ /^([-+]?\d+(?:\.\d+)?\%?)$/ ) { + $opts{lc($k)} = $v; + } + elsif ( $k =~ /^(scale)$/ + && $v =~ /^(\d+(?:\.\d+)?)(%)?$/ ) { + $opts{lc($k)} = $2 ? $1/100 : $1; + } + elsif ( $k =~ /^(center|border|spread|persist)$/i ) { + if ( $k eq "center" ) { + $opts{align} = $k; + } + else { + $opts{lc($k)} = $v; } } + elsif ( $k =~ /^(src|uri)$/i && $v ne "" ) { + $uri = $v; + } + elsif ( $k =~ /^(id)$/i && $v ne "" ) { + $id = $v; + } + elsif ( $k =~ /^(chord)$/i && $v ne "" ) { + $chord = $v; + } + elsif ( $k =~ /^(type)$/i && $v ne "" ) { + $opts{type} = $v; + } + elsif ( $k =~ /^(label)$/i && $v ne "" ) { + $opts{lc($k)} = $v; + } + elsif ( $k =~ /^(anchor)$/i + && $v =~ /^(paper|page|column|float|line)$/ ) { + $opts{lc($k)} = lc($v); + } + elsif ( $k =~ /^(align)$/i + && $v =~ /^(center|left|right)$/ ) { + $opts{lc($k)} = lc($v); + } + elsif ( $uri ) { + do_warn( "Unknown image attribute: $k\n" ); + next; + } + # Assume just an image file uri. else { - do_warn("Incomplete meta directive: $d\n") - if $config->{settings}->{strict}; - return; + $uri = $k; } - return 1; } - # Song / Global settings. - - if ( $dir eq "titles" - && $arg =~ /^(left|right|center|centre)$/i ) { - $self->{settings}->{titles} = - lc($1) eq "centre" ? "center" : lc($1); - return 1; + unless ( $uri || $id || $chord ) { + do_warn( "Missing image source\n" ); + return; } - if ( $dir eq "columns" - && $arg =~ /^(\d+)$/ ) { - # If there a column specifications in the config, retain them - # if the number of columns match. - unless( ref($config->{settings}->{columns}) eq 'ARRAY' - && $arg == @{$config->{settings}->{columns}} - ) { - $self->{settings}->{columns} = $arg; + # If the image uri does not have a directory, look it up + # next to the song, and then in the images folder of the + # resources. + if ( $uri && CP->is_here($uri) ) { + my $found = CP->siblingres( $diag->{file}, $uri, class => "images" ); + if ( $found ) { + $uri = $found; + } + else { + do_warn("Missing image for \"$uri\""); + return; } - return 1; } + $uri = "chord:$chord" if $chord; - if ( $dir eq "pagetype" || $dir eq "pagesize" ) { - $self->{settings}->{papersize} = $arg; - return 1; - } + my $aid = $id || "_Image".$assetid++; - if ( $dir eq "diagrams" ) { # AKA grid - if ( $arg ne "" ) { - $self->{settings}->{diagrams} = !!is_true($arg); - $self->{settings}->{diagrampos} = lc($arg) - if $arg =~ /^(right|bottom|top|below)$/i; + if ( defined $opts{spread} ) { + if ( exists $self->{spreadimage} ) { + do_warn("Skipping superfluous spread image"); } else { - $self->{settings}->{diagrams} = 1; + $self->{spreadimage} = + { id => $aid, space => $opts{spread} }; + warn("Got spread image $aid with $opts{spread} space\n"); } - return 1; - } - if ( $dir eq "no_grid" ) { - $self->{settings}->{diagrams} = 0; - return 1; } - if ( $dir eq "transpose" ) { - $propstack{transpose} //= []; - - if ( $arg =~ /^([-+]?\d+)\s*$/ ) { - my $new = $1; - push( @{ $propstack{transpose} }, [ $xpose, $xpose_dir ] ); - my %a = ( type => "control", - name => "transpose", - previous => [ $xpose, $xpose_dir ] - ); - $xpose += $new; - $xpose_dir = $new <=> 0; - my $m = $self->{meta}; - if ( $m->{key} ) { - my $key = $m->{key}->[-1]; - my $xp = $xpose; - $xp += $capo if $capo; - my $xpk = $self->{chordsinfo}->{$key}->transpose($xp, $xp <=> 0); - $self->{chordsinfo}->{$xpk->name} = $xpk; - $m->{key_from} = [ $m->{key_actual}->[0] ]; - $m->{key_actual} = [ $xpk->name ]; - } - $self->add( %a, value => $xpose, dir => $xpose_dir ) - if $no_transpose; + # Store as asset. + if ( $uri ) { + my $opts; + $opts->{type} = $opts{type} if $opts{type}; + $opts->{persist} = $opts{persist} if $opts{persist}; + delete $opts{$_} for qw( type persist ); + + if ( $id && %opts ) { + do_warn("Asset definition \"$id\" does not take attributes"); + return; + } + + $self->{assets} //= {}; + my $a; + if ( $uri =~ /\.(\w+)$/ && exists $config->{delegates}->{$1} ) { + my $d = $config->{delegates}->{$1}; + $a = { type => "image", + subtype => "delegate", + delegate => $d->{module}, + handler => $d->{handler}, + uri => $uri, + }; } else { - my %a = ( type => "control", - name => "transpose", - previous => [ $xpose, $xpose_dir ] - ); - my $m = $self->{meta}; - my ( $new, $dir ); - if ( @{ $propstack{transpose} } ) { - ( $new, $dir ) = @{ pop( @{ $propstack{transpose} } ) }; - } - else { - $new = 0; - $dir = $config->{settings}->{transpose} <=> 0; - } - $xpose = $new; - $xpose_dir = $dir; - if ( $m->{key} ) { - $m->{key_from} = [ $m->{key_actual}->[0] ]; - my $xp = $xpose; - $xp += $capo if $capo && $decapo; - $m->{key_actual} = - [ $self->{chordsinfo}->{$m->{key}->[-1]}->transpose($xp)->name ]; - } - if ( !@{ $propstack{transpose} } ) { - delete $m->{$_} for qw( key_from ); - } - $self->add( %a, value => $xpose, dir => $dir ) - if $no_transpose; + $a = { type => "image", + uri => $uri, + }; } - return 1; - } + $a->{opts} = $opts if $opts; + $self->{assets}->{$aid} = $a; - # More private hacks. - if ( !$options->{reference} && $d =~ /^([-+])([-\w.]+)$/i ) { - if ( $2 eq "dumpmeta" ) { - warn(::dump($self->{meta})); + if ( $config->{debug}->{images} ) { + warn("asset[$aid] type=image uri=$uri", + $a->{subtype} ? " subtype=$a->{subtype}" : (), + $a->{delegate} ? " delegate=$a->{delegate}" : (), + $opts->{persist} ? " persist" : (), + "\n"); } - $self->add( type => "set", - name => $2, - value => $1 eq "+" ? 1 : 0, - ); - return 1; + return if $id || defined $opts{spread}; # defining only } - if ( !$options->{reference} && $dir =~ /^\+([-\w.]+(?:\.[<>])?)$/ ) { - $self->add( type => "set", - name => $1, - value => $arg, - ); + if ( $opts{label} ) { + $self->add( type => "set", + name => "label", + value => $opts{label}, + context => "image" ); + push( @labels, $opts{label} ); + } - # THIS IS BASICALLY A COPY OF THE CODE IN Config.pm. - # TODO: GENERALIZE. - my $ccfg = {}; - my @k = split( /[:.]/, $1 ); - my $c = \$ccfg; # new - my $o = $config; # current - my $lk = pop(@k); # last key + $self->add( type => "image", + id => $aid, + opts => \%opts ); + return 1; +} - # Step through the keys. - foreach ( @k ) { - $c = \($$c->{$_}); - $o = $o->{$_}; +sub dir_title { + my ( $self, $dir, $arg ) = @_; + $self->{title} = $arg; + push( @{ $self->{meta}->{title} }, $arg ); + return 1; +} + +sub dir_subtitle { + my ( $self, $dir, $arg ) = @_; + push( @{ $self->{subtitle} }, $arg ); + push( @{ $self->{meta}->{subtitle} }, $arg ); + return 1; +} + +# Metadata. + +sub dir_meta { + my ( $self, $dir, $arg ) = @_; + + if ( $arg =~ /([^ :]+)[ :]+(.*)/ ) { + my $key = lc $1; + my @vals = ( $2 ); + if ( $config->{metadata}->{autosplit} ) { + @vals = map { s/s\+$//; $_ } + split( quotemeta($config->{metadata}->{separator}), $vals[0] ); } + my $m = $self->{meta}; - # Turn hash.array into hash.array.> (append). - if ( ref($o) eq 'HASH' && ref($o->{$lk}) eq 'ARRAY' ) { - $c = \($$c->{$lk}); - $o = $o->{$lk}; - $lk = '>'; + # User and instrument cannot be set here. + if ( $key eq "user" || $key eq "instrument" ) { + do_warn("\"$key\" can be set from config only.\n"); + return 1; } - # Final key. Merge array if so. - if ( ( $lk =~ /^\d+$/ || $lk eq '>' || $lk eq '<' ) - && ref($o) eq 'ARRAY' ) { - unless ( ref($$c) eq 'ARRAY' ) { - # Only copy orig values the first time. - $$c->[$_] = $o->[$_] for 0..scalar(@{$o})-1; + for my $val ( @vals ) { + + if ( $key eq "key" ) { + $val =~ s/[\[\]]//g; + my $info = do { + # When transcoding to nash/roman, parse_chord will + # complain about a missing key. Fake one. + local( $self->{meta}->{key} ) = [ '_dummy_' ]; + local( $self->{chordsinfo}->{_dummy_} ) = { root_ord => 0 }; + $self->parse_chord($val); + }; + my $name = $info->name; + my $act = $name; + + if ( $capo ) { + $act = $self->add_chord( $info->transpose($capo) ); + $name = $act if $decapo; + } + + push( @{ $m->{key} }, $name ); + $m->{key_actual} = [ $act ]; +# warn("XX key=$name act=$act capo=", +# $capo//""," decapo=$decapo\n"); + return 1; } - if ( $lk eq '>' ) { - push( @{$$c}, $arg ); + + + if ( $key eq "capo" ) { + do_warn("Multiple capo settings may yield surprising results.") + if exists $m->{capo}; + + $capo = $val || undef; + if ( $capo && $m->{key} ) { + if ( $decapo ) { + my $key = $self->store_chord + ($self->{chordsinfo}->{$m->{key}->[-1]} + ->transpose($val)); + $m->{key}->[-1] = $key; + $key = $self->store_chord + ($self->{chordsinfo}->{$m->{key}->[-1]} + ->transpose($xpose)); + $m->{key_actual} = [ $key ]; + } + else { + my $act = $m->{key_actual}->[-1]; + $m->{key_from} = [ $act ]; + my $key = $self->store_chord + ($self->{chordsinfo}->{$act}->transpose($val)); + $m->{key_actual} = [ $key ]; + } + } } - elsif ( $lk eq '<' ) { - unshift( @{$$c}, $arg ); + + elsif ( $key eq "duration" && $val ) { + $val = duration($val); } - else { - $$c->[$lk] = $arg; + + if ( $config->{metadata}->{strict} + && ! any { $_ eq $key } @{ $config->{metadata}->{keys} } ) { + # Unknown, and strict. + do_warn("Unknown metadata item: $key") + if $config->{settings}->{strict}; + return; } + + push( @{ $self->{meta}->{$key} }, $val ) if defined $val; } - else { - $$c->{$lk} = $arg; - } + } + else { + do_warn("Incomplete meta directive: $dir $arg\n") + if $config->{settings}->{strict}; + return; + } + return 1; +} - $config->augment($ccfg); - upd_config(); +# Song / Global settings. +sub dir_titles { + my ( $self, $dir, $arg ) = @_; + + unless ( $arg =~ /^(left|right|center|centre)$/i ) { + do_warn("Invalid argument for titles directive: $arg\n"); return 1; } + $self->{settings}->{titles} = lc($1) eq "centre" ? "center" : lc($1); + return 1; +} - # Formatting. {chordsize XX} and such. - if ( $dir =~ m/ ^( text | chord | chorus | tab | grid | diagrams - | title | footer | toc ) - ( font | size | colou?r ) - $/x ) { - my $item = $1; - my $prop = $2; - - $self->propset( $item, $prop, $arg ); +sub dir_columns { + my ( $self, $dir, $arg ) = @_; - # Derived props. - $self->propset( "chorus", $prop, $arg ) if $item eq "text"; - - #::dump( { %propstack, line => $diag->{line} } ); + unless ( $arg =~ /^(\d+)$/ ) { + do_warn("Invalid argument for columns directive: $arg (should be a number)\n"); return 1; } + # If there a column specifications in the config, retain them + # if the number of columns match. + unless( ref($config->{settings}->{columns}) eq 'ARRAY' + && $arg == @{$config->{settings}->{columns}} + ) { + $self->{settings}->{columns} = $arg; + } + return 1; +} - # define A: base-fret N frets N N N N N N fingers N N N N N N - # define: A base-fret N frets N N N N N N fingers N N N N N N - # optional: base-fret N (defaults to 1) - # optional: N N N N N N (for unknown chords) - # optional: fingers N N N N N N +sub dir_papersize { + my ( $self, $dir, $arg ) = @_; + $self->{settings}->{papersize} = $arg; + return 1; +} - if ( $dir eq "define" or $dir eq "chord" ) { +sub dir_diagrams { # AKA grid + my ( $self, $dir, $arg ) = @_; - return $self->define_chord( $dir, $arg ); + if ( $arg ne "" ) { + $self->{settings}->{diagrams} = !!is_true($arg); + $self->{settings}->{diagrampos} = lc($arg) + if $arg =~ /^(right|bottom|top|below)$/i; } + else { + $self->{settings}->{diagrams} = 1; + } + return 1; +} - # Warn about unknowns, unless they are x_... form. - do_warn("Unknown directive: $d\n") - if $config->{settings}->{strict} && $d !~ /^x_/; - return; +sub dir_grid { + my ( $self, $dir, $arg ) = @_; + $self->{settings}->{diagrams} = 1; + return 1; } +sub dir_no_grid { + my ( $self, $dir, $arg ) = @_; + $self->{settings}->{diagrams} = 0; + return 1; +} + +sub dir_transpose { + my ( $self, $dir, $arg ) = @_; + + $propstack{transpose} //= []; + + if ( $arg =~ /^([-+]?\d+)\s*$/ ) { + my $new = $1; + push( @{ $propstack{transpose} }, [ $xpose, $xpose_dir ] ); + my %a = ( type => "control", + name => "transpose", + previous => [ $xpose, $xpose_dir ] + ); + $xpose += $new; + $xpose_dir = $new <=> 0; + my $m = $self->{meta}; + if ( $m->{key} ) { + my $key = $m->{key}->[-1]; + my $xp = $xpose; + $xp += $capo if $capo; + my $xpk = $self->{chordsinfo}->{$key}->transpose($xp, $xp <=> 0); + $self->{chordsinfo}->{$xpk->name} = $xpk; + $m->{key_from} = [ $m->{key_actual}->[0] ]; + $m->{key_actual} = [ $xpk->name ]; + } + $self->add( %a, value => $xpose, dir => $xpose_dir ) + if $no_transpose; + } + else { + my %a = ( type => "control", + name => "transpose", + previous => [ $xpose, $xpose_dir ] + ); + my $m = $self->{meta}; + my ( $new, $dir ); + if ( @{ $propstack{transpose} } ) { + ( $new, $dir ) = @{ pop( @{ $propstack{transpose} } ) }; + } + else { + $new = 0; + $dir = $config->{settings}->{transpose} <=> 0; + } + $xpose = $new; + $xpose_dir = $dir; + if ( $m->{key} ) { + $m->{key_from} = [ $m->{key_actual}->[0] ]; + my $xp = $xpose; + $xp += $capo if $capo && $decapo; + $m->{key_actual} = + [ $self->{chordsinfo}->{$m->{key}->[-1]}->transpose($xp)->name ]; + } + if ( !@{ $propstack{transpose} } ) { + delete $m->{$_} for qw( key_from ); + } + $self->add( %a, value => $xpose, dir => $dir ) + if $no_transpose; + } + return 1; +} + +#### End of directive handlers #### + sub propset { my ( $self, $item, $prop, $value ) = @_; $prop = "color" if $prop eq "colour"; @@ -1672,6 +1909,12 @@ sub propset { # was also a size saved. Pop it. if ( $prop eq "font" && $old =~ /\s(\d+(?:\.\d+)?)$/ ) { pop( @{ $propstack{"$item-size"} } ); + $self->add( type => "control", + name => "$item-size", + value => + @{ $propstack{"$item-size"} } + ? $propstack{"$item-size"}->[-1] + : undef ) } } else { @@ -2210,6 +2453,11 @@ sub structurize { sub dump { my ( $self, $full ) = @_; + $full ||= 0; + + if ( $full == 2 ) { + return ::dump($self->{body}); + } my $a = dclone($self); $a->{config} = ref(delete($a->{config})); unless ( $full ) { @@ -2217,14 +2465,6 @@ sub dump { $a->{chordsinfo}{$ci} = $a->{chordsinfo}{$ci}->simplify; } } -# require Data::Dump::Filtered; -# warn Data::Dump::Filtered::dump_filtered($a, sub { -# my ( $ctx, $o ) = @_; -# my $h = { hide_keys => [ 'parser' ] }; -# $h->{bless} = "" -# if $ctx->class; -# $h; -# }); ::dump($a); } diff --git a/lib/ChordPro/Songbook.pm b/lib/ChordPro/Songbook.pm index cbb02ce2..c7679fd0 100644 --- a/lib/ChordPro/Songbook.pm +++ b/lib/ChordPro/Songbook.pm @@ -17,6 +17,7 @@ use ChordPro::Song; use Carp; use List::Util qw(any); use File::LoadLines; +use Storable qw(dclone); sub new { my ($pkg) = @_; @@ -63,23 +64,30 @@ sub parse_file { my $linecnt = 0; my $songs = 0; while ( @$lines ) { - my $song = ChordPro::Song - # WxChordPro uses temp file _filesource. Add real filename as well. - ->new( $opts->{filesource} || $opts->{_filesource} ) - ->parse_song( $lines, \$linecnt, {%$meta}, {%$defs} ); + my $song = ChordPro::Song->new($opts) + ->parse_song( $lines, \$linecnt, {%{dclone($meta)}}, {%$defs} ); + $song->{meta}->{songindex} = 1 + @{ $self->{songs} }; push( @{ $self->{songs} }, $song ); + $songs++; # Copy persistent assets to the songbook. if ( $song->{assets} ) { $self->{assets} //= {}; while ( my ($k,$v) = each %{$song->{assets}} ) { - next unless $v->{persist}; + next unless $v->{opts} && $v->{opts}->{persist}; $self->{assets}->{$k} = $v; } } + } - $songs++ if $song->{body}; + if ( @{$self->{songs}} > 1 ) { + my $song = $self->{songs}->[-1]; + unless ( $song->{body} + && any { $_->{type} ne "ignore" } @{$song->{body}} ) { + pop( @{ $self->{songs} } ); + $songs--; + } } warn("Warning: No songs found in ", $opts->{_filesource}, "\n") diff --git a/lib/ChordPro/Testing.pm b/lib/ChordPro/Testing.pm index efd929e9..49c2248f 100644 --- a/lib/ChordPro/Testing.pm +++ b/lib/ChordPro/Testing.pm @@ -14,15 +14,13 @@ binmode STDERR => ':utf8'; package ChordPro::Testing; -our $VERSION = "6.000"; - -use base 'Exporter'; +use parent 'Exporter'; our @EXPORT = qw( $config ); use Test::More (); -use App::Packager ( ':name', 'ChordPro' ); use ChordPro::Config; +use ChordPro::Paths; use ChordPro::Chords; sub import { @@ -32,7 +30,6 @@ sub import { -d "t" && chdir "t"; $::running_under_test = 1; - App::Packager->export_to_level(1); Test::More->export_to_level(1); $pkg->export_to_level( 1, undef, @EXPORT ); } @@ -43,7 +40,7 @@ sub is_deeply { if ( ref($got) eq 'HASH' && ref($expect) eq 'HASH' ) { fixchords($got) if $got->{body}; - for ( qw( config ) ) { + for ( qw( config generate ) ) { delete $got->{$_} unless exists $expect->{$_}; } if ( $got->{chordsinfo} ) { @@ -56,7 +53,8 @@ sub is_deeply { } } } - for ( qw( instrument user key_from key_actual chords numchords ) ) { + for ( qw( instrument user key_from key_actual chords numchords + _configversion ) ) { delete $got->{meta}->{$_} unless exists $expect->{meta}->{$_}; } } @@ -82,7 +80,7 @@ ChordPro::Chords::add_config_chord no warnings 'redefine'; sub getresource { - App::Packager::U_GetResource(@_); + CP->findres($_[0]); } } diff --git a/lib/ChordPro/Utils.pm b/lib/ChordPro/Utils.pm index 8466911b..dcef2d19 100644 --- a/lib/ChordPro/Utils.pm +++ b/lib/ChordPro/Utils.pm @@ -7,8 +7,8 @@ use utf8; use Carp; use feature qw( signatures ); no warnings "experimental::signatures"; -use parent qw(Exporter); +use Exporter 'import'; our @EXPORT; ################ Platforms ################ @@ -17,8 +17,9 @@ use constant MSWIN => $^O =~ /MSWin|Windows_NT/i ? 1 : 0; sub is_msw () { MSWIN } sub is_macos () { $^O =~ /darwin/ } +sub is_wx () { main->can("OnInit") } -push( @EXPORT, 'is_msw', 'is_macos' ); +push( @EXPORT, qw( is_msw is_macos is_wx ) ); ################ Filenames ################ diff --git a/lib/ChordPro/Version.pm b/lib/ChordPro/Version.pm index fe7ae807..acbca98f 100644 --- a/lib/ChordPro/Version.pm +++ b/lib/ChordPro/Version.pm @@ -1,4 +1,4 @@ # This file is generated. Do not edit! package ChordPro::Version; -our $VERSION = "6.030"; +our $VERSION = "6.040"; print "$VERSION\n" unless caller; diff --git a/lib/ChordPro/Wx/Main.pm b/lib/ChordPro/Wx/Main.pm index 4d6438df..a08fcc3f 100644 --- a/lib/ChordPro/Wx/Main.pm +++ b/lib/ChordPro/Wx/Main.pm @@ -10,15 +10,15 @@ package ChordPro::Wx::Main; # ChordPro::Wx::Main_wxg is generated by wxGlade and contains # all UI associated code. -use base qw( ChordPro::Wx::Main_wxg ); +use parent qw( ChordPro::Wx::Main_wxg ); use Wx qw[:everything]; use Wx::Locale gettext => '_T'; use ChordPro; +use ChordPro::Paths; use ChordPro::Output::Common; use ChordPro::Utils qw( demarkup is_macos ); -use App::Packager; use File::Temp qw( tempfile ); use Encode qw(decode_utf8); @@ -30,6 +30,11 @@ sub new { Wx::Event::EVT_IDLE($self, $self->can('OnIdle')); Wx::Event::EVT_CLOSE($self, $self->can('OnClose')); + # By default the TextCtrl on MacOS substitutes smart quotes and dashes. + # Note that OSXDisableAllSmartSubstitutions requires an augmented + # version of wxPerl. + $self->{t_source}->OSXDisableAllSmartSubstitutions + if $self->{t_source}->can("OSXDisableAllSmartSubstitutions"); $self; } @@ -175,30 +180,53 @@ sub init { # List of available config presets (styles). my $stylelist; sub stylelist { + my ( $self ) = @_; return $stylelist if $stylelist && @$stylelist; - my $cfglib = getresource("config"); - $stylelist = []; - if ( -d $cfglib ) { + my $cfglib = CP->configdir; + my @stylelist; + my %stylelist; + for my $cfglib ( @{ CP->findresdirs("config") } ) { + next unless $cfglib && -d $cfglib; opendir( my $dh, $cfglib ); - foreach ( sort readdir($dh) ) { + foreach ( readdir($dh) ) { + $_ = decode_utf8($_); + next unless /^(.*)\.json$/; + my $base = $1; + $stylelist{$base} = $_; + } + } + + my $dir = $self->{prefs_customlib}; + if ( $dir && -d ( $cfglib = "$dir/config" ) ) { + opendir( my $dh, $cfglib ); + foreach ( readdir($dh) ) { $_ = decode_utf8($_); next unless /^(.*)\.json$/; my $base = $1; - unshift( @$stylelist, $base ), next - if $base eq "chordpro"; # default - push( @$stylelist, $base ); + $stylelist{$base} = " $_"; + } + } + + push( @stylelist, "chordpro" ) if delete $stylelist{chordpro}; + foreach ( sort keys %stylelist ) { + if ( $stylelist{$_} =~ /^\s+(.*)/ ) { + push( @stylelist, "$_ (User)" ); + } + else { + push( @stylelist, "$_" ); } } - return $stylelist; + + return $stylelist = \@stylelist; } # List of available notation systems. my $notationlist; sub notationlist { return $notationlist if $notationlist && @$notationlist; - my $cfglib = getresource("config/notes"); $notationlist = [ undef ]; - if ( -d $cfglib ) { + for my $cfglib ( @{ CP->findresdirs( "notes", class => "config" ) } ) { + next unless $cfglib && -d $cfglib; opendir( my $dh, $cfglib ); foreach ( sort readdir($dh) ) { $_ = decode_utf8($_); @@ -216,48 +244,50 @@ my @tasks; sub setup_tasks { my ( $self ) = @_; - my $dir = $self->{prefs_customlib}; - return unless $dir && -d $dir; - $dir .= "/tasks"; - return unless $dir && -d $dir; - - use File::Glob 'bsd_glob'; - use File::Basename; - - my @files = glob( "$dir/*.{json,prp}" ); - return unless @files; my $menu = $self->{main_menubar}->FindMenu("Tasks"); $menu = $self->{main_menubar}->GetMenu($menu); + my @libs = @{ CP->findresdirs("tasks") }; + my $dir = $self->{prefs_customlib}; + push( @libs, "$dir/tasks" ) if $dir && -d "$dir/tasks"; my $did; - foreach my $file ( @files ) { - next unless -s $file; - - # Tentative title (description). - ( my $desc = basename( $file, ".json", ".prp" ) ) =~ s/_/ /g; - - # Peek in the first line. - my $line; - my $fd; - open( $fd, '<:utf8', $file ) and - $line = <$fd> and - close($fd); - if ( $line =~ m;(?://|\#)\s*(?:chordpro\s*)?task:\s*(.*);i ) { - $desc = $1; + my %dups; + for my $cfglib ( @libs ) { + next unless $cfglib && -d $cfglib; + opendir( my $dh, $cfglib ); + foreach ( readdir($dh) ) { + $_ = decode_utf8($_); + next unless /^(.*)\.(?:json|prp)$/; + my $base = $1; + my $file = File::Spec->catfile( $cfglib, $_ ); + + # Tentative title (description). + ( my $desc = $base ) =~ s/_/ /g; + + # Peek in the first line. + my $line; + my $fd; + open( $fd, '<:utf8', $file ) and + $line = <$fd> and + close($fd); + if ( $line =~ m;(?://|\#)\s*(?:chordpro\s*)?task:\s*(.*);i ) { + $desc = $1; + } + next unless $dups{$desc}++; + + # Append to the menu, first a separator if needed. + $menu->AppendSeparator unless $did++; + my $id = Wx::NewId(); + $menu->Append( $id, $desc, _T("Custom task: ").$desc ); + Wx::Event::EVT_MENU + ( $self, $id, + sub { + my ( $self, $event ) = @_; + $self->preview( "--config", $file ); + } ); + push( @tasks, [ $desc, $file ] ); } - - # Append to the menu, first a separator if needed. - $menu->AppendSeparator unless $did++; - my $id = Wx::NewId(); - $menu->Append( $id, $desc, _T("Custom task: ").$desc ); - Wx::Event::EVT_MENU - ( $self, $id, - sub { - my ( $self, $event ) = @_; - $self->preview( "--config", $file ); - } ); - push( @tasks, [ $desc, $file ] ); } } @@ -390,13 +420,13 @@ sub preview { $SIG{__WARN__} = \&_warn unless $self->{_log}; # $SIG{__DIE__} = \&_die; - my $haveconfig; + my $haveconfig = List::Util::any { $_ eq "--config" } @opts; if ( $self->{prefs_skipstdcfg} ) { push( @ARGV, '--nodefaultconfigs' ); } if ( $self->{prefs_cfgpreset} ) { foreach ( @{ $self->{prefs_cfgpreset} } ) { - push( @ARGV, '--config', $_ ); + push( @ARGV, '--config', $_ =~ s/ \(User\)//ir ); $haveconfig++; } } @@ -567,8 +597,8 @@ sub GetPreferences { $self->{_cfgpresetfile} = $self->{prefs_configfile}; } my @presets; - foreach ( @{stylelist()} ) { - if ( ",$p" =~ quotemeta( "," . $_ ) ) { + foreach ( @{$self->stylelist()} ) { + if ( ",$p" =~ quotemeta( "," . lc($_) ) ) { push( @presets, $_ ); } } @@ -727,7 +757,7 @@ sub OnHelp_Config { sub OnHelp_Example { my ($self, $event) = @_; return unless $self->checksaved; - $self->openfile( getresource( "examples/swinglow.cho" ) ); + $self->openfile( CP->findres( "swinglow.cho", class => "examples" ) ); undef $self->{_currentfile}; $self->{t_source}->SetModified(0); @@ -830,7 +860,7 @@ sub _aboutmsg { return $msg; } -sub OnAbout { +sub xOnAbout { my ($self, $event) = @_; my $md = Wx::MessageDialog->new @@ -842,6 +872,21 @@ sub OnAbout { $md->Destroy; } +sub OnAbout { + my ($self, $event) = @_; + + # Need a custom dialog since the mesage doesn't look well in + # a non-proportional font. + my $md = AboutDialog->new + ( $self, -1, "About ChordPro", + wxDefaultPosition, wxDefaultSize, undef, + "About", + _aboutmsg() + ); + $md->ShowModal; + $md->Destroy; +} + sub OnIdle { my ( $self, $event ) = @_; my $f = $self->{_windowtitle} // ""; @@ -851,4 +896,65 @@ sub OnIdle { ################ End of Event handlers ################ +package AboutDialog; + +use Wx qw[:everything]; +use parent -norequire, qw(Wx::Dialog); +use strict; + +sub new { + my( $self, $parent, $id, $title, $pos, $size, $style, $name, $text ) = @_; + $parent = undef unless defined $parent; + $id = -1 unless defined $id; + $title = "" unless defined $title; + $pos = wxDefaultPosition unless defined $pos; + $size = wxDefaultSize unless defined $size; + $name = "" unless defined $name; + + # begin wxGlade: MyDialog::new + $style = wxDEFAULT_DIALOG_STYLE + unless defined $style; + + $self = $self->SUPER::new( $parent, $id, $title, $pos, $size, $style, $name ); + + # Infer size from text. + my ( $lc, $ll ) = ( 0, 0 ); + for ( split( /[\r\n]+/, $text ) ) { + $lc++; + $ll = length($_) if length($_) > $ll; + } + $self->SetSize(Wx::Size->new(10*$ll, 24*$lc)); + + $self->SetTitle("About ChordPro"); + $self->SetFont(Wx::Font->new(11, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, 0, "")); + + $self->{sizer_1} = Wx::BoxSizer->new(wxVERTICAL); + + $text =~ s/[\r\n]+$//; + $self->{text} = Wx::TextCtrl->new($self, wxID_ANY, $text, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY); + $self->{text}->SetFont(Wx::Font->new(11, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, 0, "")); + $self->{text}->SetInsertionPoint(0); + $self->{sizer_1}->Add($self->{text}, 1, wxEXPAND|wxLEFT|wxRIGHT|wxTOP, 5); + + $self->{sizer_2} = Wx::BoxSizer->new(wxHORIZONTAL); + $self->{sizer_1}->Add($self->{sizer_2}, 0, wxALIGN_RIGHT|wxALL, 4); + + $self->{sizer_2}->Add(1, 1, 0, wxEXPAND, 0); + + $self->{button_OK} = Wx::Button->new($self, wxID_OK, ""); + $self->{button_OK}->SetDefault(); + $self->{sizer_2}->Add($self->{button_OK}, 0, 0, 0); + + $self->SetSizer($self->{sizer_1}); + + $self->SetAffirmativeId($self->{button_OK}->GetId()); + + $self->Layout(); + # end wxGlade + return $self; + +} + +# end of class AboutDialog + 1; diff --git a/lib/ChordPro/Wx/Main_wxg.pm b/lib/ChordPro/Wx/Main_wxg.pm index 7c4fa6c0..099ec8bf 100644 --- a/lib/ChordPro/Wx/Main_wxg.pm +++ b/lib/ChordPro/Wx/Main_wxg.pm @@ -15,7 +15,7 @@ use Wx::Locale gettext => '_T'; package ChordPro::Wx::Main_wxg; use Wx qw[:everything]; -use base qw(Wx::Frame); +use parent -norequire, qw(Wx::Frame); use strict; # begin wxGlade: dependencies diff --git a/lib/ChordPro/Wx/PreferencesDialog.pm b/lib/ChordPro/Wx/PreferencesDialog.pm index 098338f9..637f3062 100644 --- a/lib/ChordPro/Wx/PreferencesDialog.pm +++ b/lib/ChordPro/Wx/PreferencesDialog.pm @@ -11,11 +11,10 @@ package ChordPro::Wx::PreferencesDialog; # ChordPro::Wx::PreferencesDialog_wxg is generated by wxGlade and contains # all UI associated code. -use base qw( ChordPro::Wx::PreferencesDialog_wxg ); +use parent qw( ChordPro::Wx::PreferencesDialog_wxg ); use Wx qw[:everything]; use Wx::Locale gettext => '_T'; -use App::Packager; use ChordPro::Utils qw(is_macos); # BUilt-in descriptions for some notation systems. @@ -219,7 +218,7 @@ sub store_prefs { # Custom library. $parent->{prefs_enable_customlib} = $self->{cb_customlib}->IsChecked; - $parent->{prefs_customlib} = $ENV{CHORDPRO_LIB} // $self->{l_customlibdialog}->GetValue; + $parent->{prefs_customlib} = $ENV{CHORDPRO_LIB} // $self->{t_customlibdialog}->GetValue; # New song template. $parent->{prefs_enable_tmplfile} = $self->{cb_tmplfile}->IsChecked; @@ -281,7 +280,7 @@ sub OnConfigFileDialog { my $fd = Wx::FileDialog->new ($self, _T("Choose config file"), "", $self->GetParent->{prefs_configfile} || "", - "Config files (*.cfg,*.json)|*.cfg;*.json|All files|*.*", + "Config files (*.prp,*.json)|*.prp;*.json|All files|*.*", 0|wxFD_OPEN, wxDefaultPosition); my $ret = $fd->ShowModal; @@ -339,6 +338,7 @@ sub OnCustomLibDialog { if ( $ret == wxID_OK ) { my $file = $fd->GetPath; $self->{t_customlibdialog}->SetValue($file); + $ENV{CHORDPRO_LIB} = $file; } $fd->Destroy; } diff --git a/lib/ChordPro/Wx/PreferencesDialog_wxg.pm b/lib/ChordPro/Wx/PreferencesDialog_wxg.pm index c8df5aef..de5b6c3c 100644 --- a/lib/ChordPro/Wx/PreferencesDialog_wxg.pm +++ b/lib/ChordPro/Wx/PreferencesDialog_wxg.pm @@ -15,7 +15,7 @@ use strict; package ChordPro::Wx::PreferencesDialog_wxg; use Wx qw[:everything]; -use base qw(Wx::Dialog); +use parent -norequire, qw(Wx::Dialog); use strict; use Wx::Locale gettext => '_T'; diff --git a/lib/ChordPro/Wx/RenderDialog.pm b/lib/ChordPro/Wx/RenderDialog.pm index 7f055b88..7b8489e1 100644 --- a/lib/ChordPro/Wx/RenderDialog.pm +++ b/lib/ChordPro/Wx/RenderDialog.pm @@ -11,11 +11,10 @@ package ChordPro::Wx::RenderDialog; # ChordPro::Wx::RenderDialog_wxg is generated by wxGlade and contains # all UI associated code. -use base qw( ChordPro::Wx::RenderDialog_wxg ); +use parent qw( ChordPro::Wx::RenderDialog_wxg ); use Wx qw[:everything]; use Wx::Locale gettext => '_T'; -use App::Packager; # BUilt-in descriptions for some notation systems. my $notdesc = diff --git a/lib/ChordPro/Wx/RenderDialog_wxg.pm b/lib/ChordPro/Wx/RenderDialog_wxg.pm index aed9ed5b..a70ca0b0 100644 --- a/lib/ChordPro/Wx/RenderDialog_wxg.pm +++ b/lib/ChordPro/Wx/RenderDialog_wxg.pm @@ -15,7 +15,7 @@ use strict; package ChordPro::Wx::RenderDialog_wxg; use Wx qw[:everything]; -use base qw(Wx::Dialog); +use parent -norequire, qw(Wx::Dialog); use strict; use Wx::Locale gettext => '_T'; diff --git a/lib/ChordPro/lib/SVGPDF.pm b/lib/ChordPro/lib/SVGPDF.pm index 4925bb7f..c63e7581 100644 --- a/lib/ChordPro/lib/SVGPDF.pm +++ b/lib/ChordPro/lib/SVGPDF.pm @@ -7,7 +7,7 @@ use utf8; class SVGPDF; -our $VERSION = '0.070'; +our $VERSION = '0.083'; =head1 NAME @@ -46,6 +46,16 @@ with L, L and compatible PDF packages. The main method is process(). It takes the SVG from an input source, see L. +=head1 COORDINATES & UNITS + +SVG coordinates run from top-left to bottom-right. + +Dimensions without units are B, at 96 pixels / inch. E.g., +C means 96px (pixels) and is equal to 72pt (points) or 1in (inch). + +For font sizes, CSS defines C to be equal to the font size, and +C is half of the font size. + =head1 CONSTRUCTOR In its most simple form, a new SVGPDF object can be created with a @@ -53,7 +63,7 @@ single argument, the PDF document. $svg = SVGPDF->new($pdf); -There are a few additional arguments, these must be specified as +There are a few optional arguments, these can be specified as key/value pairs. =over 8 @@ -230,6 +240,27 @@ The font size to be used for dimensions in 'ex' and 'em' units. This value overrides the value set in the constructor. +=item combine + +An SVG can produce multiple XObjects, but sometimes these should be +kept as a single image. + +There are two ways to combine the image objects. This can be selected +by setting $opts{combine} to either C<"stacked"> or C<"bbox">. + +Type C<"stacked"> (default) stacks the images on top of each other, +left sides aligned. The bounding box of each object is only used to +obtain the width and height. + +Type C<"bbox"> stacks the images using the bounding box details. The +origins of the images are vertically aligned and images may protrude +other images when the image extends below the origin. + +=item sep + +When combining images, add additional vertical space between the +individual images. + =back =cut @@ -260,6 +291,13 @@ method process ( $data, %options ) { # Restore. $fontsize = $save_fontsize; + my $combine = $options{combine} // "none"; + if ( $combine ne "none" && @$xoforms > 1 ) { + my $sep = $options{sep} || 0; + $xoforms = $self->combine_svg( $xoforms, + type => $combine, sep => $sep ); + } + # Return (hopefully a stack of XObjects). return $xoforms; } @@ -519,6 +557,68 @@ method handle_svg ( $e ) { $self->_dbg( "==== end ", $e->{name}, " ====" ); } +sub min( $a, $b ) { $a < $b ? $a : $b } +sub max( $a, $b ) { $a > $b ? $a : $b } + +method combine_svg( $forms, %opts ) { + my $type = $opts{type} // "stacked"; + return $forms if $type eq "none"; + + my ( $xmin, $ymin, $xmax, $ymax ); + my $y = 0; + my $x = 0; + my $sep = $opts{sep} || 0; + my $nx; + + if ( $type eq "bbox" ) { + warn("Combining ", scalar(@$forms), " XObjects\n") + if $verbose; + ...; + $nx->bbox( $xmin, $ymax, $xmax, 0 ); + } + else { + my $i = 0; + for my $xo ( @$forms ) { + $xo = $xo->{xo}; + my @bb = $xo->bbox; + my $w = abs( $bb[2] - $bb[0] ); + my $h = abs( $bb[3] - $bb[1] ); + + $nx //= $pdf->xo_form; + my @xy = ( $x - min($bb[0],$bb[2]), $y - max($bb[1],$bb[3]) ); + warn(sprintf("Stack obj %2d: %.2f %.2f %.2f %.2f \@ %.2f %.2f\n", + ++$i, @bb, @xy ) ) if $verbose; + $nx->object( $xo, @xy ); + $y -= $h; + + if ( defined $xmax ) { + $xmax = $w if $w > $xmax; + } + else { + $xmax = $w; + $xmin = 0; + } + $ymax = $y; + $y -= $sep; + } + warn("Stacked ", scalar(@$forms), " XObjects => bb", + ( map { sprintf(" %.2f", $_ ) } $xmin, $ymax, $xmax, 0 ), + "\n") + if $verbose; + $nx->bbox( $xmin, $ymax, $xmax, 0 ); + } + + + return [ { xo => $nx, + width => $xmax - $xmin, + height => -$ymax, + vwidth => $xmax - $xmin, + vheight => -$ymax, + bbox => [ $xmin, $ymax, $xmax, 0 ], + vbox => [ $xmin, -$ymax, $xmax-$xmin, $ymax ], + } ]; +} + ################ Service ################ method vb2bb( @vb ) { @@ -836,6 +936,10 @@ Shades, gradients, patterns and animations. =item * +Shape rendering attributes. + +=item * + Transparency. =item * diff --git a/lib/ChordPro/lib/SVGPDF/Element.pm b/lib/ChordPro/lib/SVGPDF/Element.pm index 4204b216..e1272b6a 100644 --- a/lib/ChordPro/lib/SVGPDF/Element.pm +++ b/lib/ChordPro/lib/SVGPDF/Element.pm @@ -107,6 +107,29 @@ method set_transform ( $tf ) { } } +# +# +# +# result = multiply_matrices( $m1, $m2, $m3 ); + +method multiply_matrices :common (@m) { + my $i = @m; + my $m2 = pop(@m); + die("Matrix$i must have 6 elements\n") unless @$m2 == 6; + + while ( --$i > 0 ) { + my $m1 = pop(@m); + die("Matrix$i must have 6 elements\n") unless @$m1 == 6; + $m2 = [ $m1->[0] * $m2->[0] + $m1->[2] * $m2->[1], + $m1->[1] * $m2->[0] + $m1->[3] * $m2->[1], + $m1->[0] * $m2->[2] + $m1->[2] * $m2->[3], + $m1->[1] * $m2->[2] + $m1->[3] * $m2->[3], + $m1->[0] * $m2->[4] + $m1->[2] * $m2->[5] + $m1->[4], + $m1->[1] * $m2->[4] + $m1->[3] * $m2->[5] + $m1->[5] ]; + } + $m2; +} + method set_graphics () { my $msg = $name; @@ -244,7 +267,10 @@ method _paintsub () { }; } else { - return sub {}; + return sub { + $self->_dbg("xo end"); + $xo->end; + } } } diff --git a/lib/ChordPro/res/abc/.manifest b/lib/ChordPro/res/abc/.manifest new file mode 100644 index 00000000..ecf52632 --- /dev/null +++ b/lib/ChordPro/res/abc/.manifest @@ -0,0 +1,34 @@ +abc2svg/abc2svg-1.js +abc2svg/abc2svg.ttf +abc2svg/abcdoc-1.js +abc2svg/ambitus-1.js +abc2svg/break-1.js +abc2svg/capo-1.js +abc2svg/chordnames-1.js +abc2svg/clair-1.js +abc2svg/clip-1.js +abc2svg/cmdline.js +abc2svg/combine-1.js +abc2svg/COPYING +abc2svg/COPYING.LESSER +abc2svg/diag-1.js +abc2svg/equalbars-1.js +abc2svg/err-en.js +abc2svg/grid-1.js +abc2svg/grid2-1.js +abc2svg/grid3-1.js +abc2svg/jazzchord-1.js +abc2svg/jianpu-1.js +abc2svg/mdnn-1.js +abc2svg/MIDI-1.js +abc2svg/nns-1.js +abc2svg/page-1.js +abc2svg/pedline-1.js +abc2svg/perc-1.js +abc2svg/psvg-1.js +abc2svg/roman-1.js +abc2svg/soloffs-1.js +abc2svg/sth-1.js +abc2svg/strtab-1.js +abc2svg/temper-1.js +abc2svg/tohtml.js diff --git a/lib/ChordPro/res/abc/abc2svg/COPYING b/lib/ChordPro/res/abc/abc2svg/COPYING new file mode 100644 index 00000000..7ea9d8df --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/COPYING @@ -0,0 +1,675 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff --git a/lib/ChordPro/res/abc/abc2svg/COPYING.LESSER b/lib/ChordPro/res/abc/abc2svg/COPYING.LESSER new file mode 100644 index 00000000..82b377b1 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/COPYING.LESSER @@ -0,0 +1,166 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + diff --git a/lib/ChordPro/res/abc/abc2svg/MIDI-1.js b/lib/ChordPro/res/abc/abc2svg/MIDI-1.js new file mode 100644 index 00000000..40128312 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/MIDI-1.js @@ -0,0 +1,136 @@ +//MIDI.js-module to handle the%%MIDI parameters +if(typeof abc2svg=="undefined") +var abc2svg={} +abc2svg.MIDI={do_midi:function(parm){function tb40(qs){var i,n1=[2,25,8,31,14,37,20,3,26,9,32,15,38,21,4,27,10,33,16,39],n2=[0,19,36,13,30,7,24,1,18,35,12,29,6,23,0,17],da=21-3*qs +b=new Float32Array(40) +for(i=0;i127){abc.syntax(1,abc.errs.bad_val,"%%MIDI chordvol") +break} +if(!cfmt.chord) +cfmt.chord={} +cfmt.chord.vol=v/127 +break +case"gchordon":case"gchordoff":if(!cfmt.chord) +cfmt.chord={} +if(abc.parse.state>=2&&curvoice){s=abc.new_block("midigch") +s.play=s.invis=1 +s.on=a[1][7]=='n'}else{cfmt.chord.gchon=a[1][7]=='n'} +break +case"channel":v=parseInt(a[2]) +if(isNaN(v)||v<=0||v>16){abc.syntax(1,abc.errs.bad_val,"%%MIDI channel") +break} +v-- +if(abc.parse.state==3){s=abc.new_block("midiprog") +s.play=s.invis=1 +curvoice.chn=s.chn=v}else{abc.set_v_param("channel",v)} +break +case"drummap":v=Number(a[3]) +if(isNaN(v)){abc.syntax(1,abc.errs.bad_val,"%%MIDI drummap") +break} +n=["C","^C","D","_E","E","F","^F","G","^G","A","_B","B"][v%12] +while(v<60){n+=',' +v+=12} +while(v>72){n+="'" +v-=12} +this.do_pscom("map MIDIdrum "+a[2]+" play="+n) +abc.set_v_param("mididrum","MIDIdrum") +break +case"program":if(a[3]!=undefined){abc2svg.MIDI.do_midi.call(abc,"MIDI channel "+a[2]) +v=a[3]}else{v=a[2]} +v=parseInt(v) +if(isNaN(v)||v<0||v>127){abc.syntax(1,abc.errs.bad_val,"%%MIDI program") +break} +if(abc.parse.state==3){s=abc.new_block("midiprog");s.play=s.invis=1 +s.instr=v +s.chn=curvoice.chn}else{abc.set_v_param("instr",v)} +break +case"control":n=parseInt(a[2]) +if(isNaN(n)||n<0||n>127){abc.syntax(1,"Bad controller number in %%MIDI") +break} +v=parseInt(a[3]) +if(isNaN(v)||v<0||v>127){abc.syntax(1,"Bad controller value in %%MIDI") +break} +if(abc.parse.state==3){s=abc.new_block("midictl");s.play=s.invis=1 +s.ctrl=n;s.val=v}else{abc.set_v_param("midictl",a[2]+' '+a[3])} +break +case"temperamentequal":n=parseInt(a[2]) +if(isNaN(n)||n<5||n>255){abc.syntax(1,abc.errs.bad_val,"%%MIDI "+a[1]) +return} +if(n==53){s=abc.get_glyphs() +s.acc12_53='' +s.acc24_53='\ + 2' +s.acc36_53='\ + 3' +s.acc48_53='' +s.acc60_53='\n\ + \n\ + \n\ +' +s["acc-60_53"]='' +s["acc-48_53"]='\n\ + \n\ + \n\ +' +s["acc-36_53"]='\n\ + \ + 3\n\ + \n\ +' +s["acc-24_53"]='\ + 2' +s["acc-12_53"]=''} +q=7.019550008653874,o=12 +cfmt.nedo=n +qs=((n*q/o+.5)|0)*o/n +if(qs<6.85||qs>7.2) +abc.syntax(0,abc.errs.bad_val,"%%MIDI "+a[1]) +cfmt.temper=tb40(qs) +break}},set_vp:function(of,a){var i,item,s,abc=this,curvoice=abc.get_curvoice() +of(a.slice(0)) +for(i=0;i=0){i=l=0 +while(1){j=parse.file.indexOf('\n',i) +if(j<0||j>idx) +break +l++;i=j+1} +c=idx-i} +h="" +if(fn){h=fn +if(l) +h+=":"+(l+1)+":"+(c+1);h+=" "} +switch(sev){case 0:h+="Warning: ";break +case 1:h+="Error: ";break +default:h+="Internal bug: ";break} +user.errmsg(h+txt,l,c)} +function error(sev,s,msg,a1,a2,a3,a4){var i,j,regex,tmp +if(!sev&&cfmt.quiet) +return +if(s){if(s.err) +return +s.err=true} +if(user.textrans){tmp=user.textrans[msg] +if(tmp) +msg=tmp} +if(arguments.length>3) +msg=msg.replace(/\$./g,function(a){switch(a){case'$1':return a1 +case'$2':return a2 +case'$3':return a3 +default:return a4}}) +if(s&&s.fname) +errbld(sev,msg,s.fname,s.istart) +else +errbld(sev,msg)} +function scanBuf(){this.index=0;scanBuf.prototype.char=function(){return this.buffer[this.index]} +scanBuf.prototype.next_char=function(){return this.buffer[++this.index]} +scanBuf.prototype.get_int=function(){var val=0,c=this.buffer[this.index] +while(c>='0'&&c<='9'){val=val*10+Number(c);c=this.next_char()} +return val}} +function syntax(sev,msg,a1,a2,a3,a4){var s={fname:parse.fname,istart:parse.istart+parse.line.index} +error(sev,s,msg,a1,a2,a3,a4)} +function js_inject(js){eval('"use strict";\n'+js)} +var dd_tb={},a_de,cross +var decos={dot:"0 stc 6 1.5 1",tenuto:"0 emb 6 4 3",slide:"1 sld 3 7 1",arpeggio:"2 arp 12 10 3",roll:"3 roll 5,4 5 6",lowermordent:"3 lmrd 6,5 4 6",uppermordent:"3 umrd 6,5 4 6",trill:"3 trl 14 5 8",upbow:"3 upb 10,2 3 7",downbow:"3 dnb 9 4 6",gmark:"3 grm 7 4 6",wedge:"0 wedge 8 1.5 1",longphrase:"5 lphr 0 1 16",mediumphrase:"5 mphr 0 1 16",shortphrase:"5 sphr 0 1 16",turnx:"3 turnx 7,2.5 5 6",invertedturn:"3 turn 7,2 5 6","0":"3 fng 5,5 3 3 0","1":"3 fng 5,5 3 3 1","2":"3 fng 5,5 3 3 2","3":"3 fng 5,5 3 3 3","4":"3 fng 5,5 3 3 4","5":"3 fng 5,5 3 3 5",plus:"3 dplus 8,2 2 4","+":"3 dplus 8,2 2 4",">":"5 accent 3.5,3.5 4 4",accent:"5 accent 3.5,3.5 4 4",emphasis:"5 accent 3.5,3.5 4 4",marcato:"3 marcato 9 5 5","^":"3 marcato 9 5 5",mordent:"3 lmrd 6,5 4 6",open:"3 opend 8 3 3",snap:"3 snap 10 3 3",thumb:"3 thumb 10 3 3",turn:"3 turn 7,2.5 5 6","trill(":"5 ltr 8 0 0","trill)":"5 ltr 8 0 0","8va(":"5 8va 12 6 6","8va)":"5 8va 12 6 6","8vb(":"4 8vb 10,5 6 6","8vb)":"4 8vb 10,5 6 6","15ma(":"5 15ma 12 9 9","15ma)":"5 15ma 12 9 9","15mb(":"4 15mb 12 9 9","15mb)":"4 15mb 12 9 9",breath:"5 brth 0 1 16",caesura:"5 caes 0 1 20",short:"5 short 0 1 16",tick:"5 tick 0 1 16",coda:"5 coda 22,5 10 10",dacapo:"5 dacs 16 20 20 Da Capo",dacoda:"5 dacs 16 20 20 Da Coda","D.C.":"5 dcap 16,3 6 6","D.S.":"5 dsgn 16,3 6 6","D.C.alcoda":"5 dacs 16 32 32 D.C. al Coda","D.S.alcoda":"5 dacs 16 32 32 D.S. al Coda","D.C.alfine":"5 dacs 16 32 32 D.C. al Fine","D.S.alfine":"5 dacs 16 32 32 D.S. al Fine",fermata:"5 hld 12 7.5 7.5",fine:"5 dacs 16 12 12 Fine",invertedfermata:"7 hld 12 8 8",segno:"5 sgno 22,2 5 5",f:"6 f 12,5 3 4",ff:"6 ff 12,5 8 5",fff:"6 fff 12,5 11 9",ffff:"6 ffff 12,5 15 12",mf:"6 mf 12,5 8 10",mp:"6 mp 12,5 9 10",p:"6 p 12,5 3 6",pp:"6 pp 12,5 8 9",ppp:"6 ppp 12,5 14 11",pppp:"6 pppp 12,5 14 17",pralltriller:"3 umrd 6,5 4 6",sfz:"6 sfz 12,5 9 9",ped:"7 ped 14 6 10","ped-up":"7 pedoff 12 4 4","ped(":"7 lped 14 1 1","ped)":"7 lped 14 1 1","crescendo(":"6 cresc 15,2 0 0","crescendo)":"6 cresc 15,2 0 0","<(":"6 cresc 15,2 0 0","<)":"6 cresc 15,2 0 0","diminuendo(":"6 dim 15,2 0 0","diminuendo)":"6 dim 15,2 0 0",">(":"6 dim 15,2 0 0",">)":"6 dim 15,2 0 0","-(":"8 gliss 0 0 0","-)":"8 gliss 0 0 0","~(":"8 glisq 0 0 0","~)":"8 glisq 0 0 0",invisible:"32 0 0 0 0",beamon:"33 0 0 0 0",trem1:"34 0 0 0 0",trem2:"34 0 0 0 0",trem3:"34 0 0 0 0",trem4:"34 0 0 0 0",xstem:"35 0 0 0 0",beambr1:"36 0 0 0 0",beambr2:"36 0 0 0 0",rbstop:"37 0 0 0 0","/":"38 0 0 6 6","//":"38 0 0 6 6","///":"38 0 0 6 6","beam-accel":"39 0 0 0 0","beam-rall":"39 0 0 0 0",stemless:"40 0 0 0 0",rbend:"41 0 0 0 0",editorial:"42 0 0 0 0","sacc-1":"3 sacc-1 6,4 4 4",sacc3:"3 sacc3 6,5 4 4",sacc1:"3 sacc1 6,4 4 4",courtesy:"43 0 0 0 0","cacc-1":"3 cacc-1 0 0 0",cacc3:"3 cacc3 0 0 0",cacc1:"3 cacc1 0 0 0","tie(":"44 0 0 0 0","tie)":"44 0 0 0 0"},f_near=[d_near,d_slide,d_arp],f_note=[null,null,null,d_upstaff,d_upstaff],f_staff=[null,null,null,null,null,d_upstaff,d_upstaff,d_upstaff] +function y_get(st,up,x,w){var y,p_staff=staff_tb[st],i=(x/2)|0,j=((x+w)/2)|0 +if(i<0) +i=0 +if(j>=YSTEP){j=YSTEP-1 +if(i>j) +i=j} +if(up){y=p_staff.top[i++] +while(i<=j){if(yp_staff.bot[i]) +y=p_staff.bot[i];i++}} +return y} +function y_set(st,up,x,w,y){var p_staff=staff_tb[st],i=(x/2)|0,j=((x+w)/2)|0 +if(i<0) +i=0 +if(j>=YSTEP){j=YSTEP-1 +if(i>j) +i=j} +if(up){while(i<=j){if(p_staff.top[i]y) +p_staff.bot[i]=y;i++}}} +function up3(s,pos){switch(pos&0x07){case C.SL_ABOVE:return 1 +case C.SL_BELOW:return 0} +return!s.second} +function up6(s,pos){switch(pos&0x07){case C.SL_ABOVE:return true +case C.SL_BELOW:return false} +if(s.multi) +return s.multi>0 +if(!s.p_v.have_ly) +return false +return(s.pos.voc&0x07)!=C.SL_ABOVE} +function d_arp(de){var m,h,dx,s=de.s,dd=de.dd,xc=dd.wr +if(s.type==C.NOTE){for(m=0;m<=s.nhd;m++){if(s.notes[m].acc){dx=s.notes[m].shac}else{dx=1-s.notes[m].shhd +switch(s.head){case C.SQUARE:dx+=3.5 +break +case C.OVALBARS:case C.OVAL:dx+=2 +break}} +if(dx>xc) +xc=dx}} +h=3*(s.notes[s.nhd].pit-s.notes[0].pit)+4;m=dd.h +if(h0&&y<24){y=(((y+9)/6)|0)*6-6} +if(up){y+=dd.hd +s.ymx=y+dd.h}else if(dd.name[0]=='w'){de.inv=true +y-=dd.h +s.ymn=y}else{y-=dd.h +s.ymn=y-dd.hd} +de.x-=dd.wl +de.y=y +if(s.type==C.NOTE) +de.x+=s.notes[s.stem>=0?0:s.nhd].shhd +if(dd.name[0]=='d'){if(!(s.beam_st&&s.beam_end)){if(up){if(s.stem>0) +de.x+=3.5}else{if(s.stem<0) +de.x-=3.5}}else{if(up&&s.stem>0){y=s.y+(y-s.y)*.6 +if(y>=27){de.y=y +s.ymx=de.y+dd.h}}}}} +function d_slide(de){var m,dx,s=de.s,yc=s.notes[0].pit,xc=5 +for(m=0;m<=s.nhd;m++){if(s.notes[m].acc){dx=4+s.notes[m].shac}else{dx=5-s.notes[m].shhd +switch(s.head){case C.SQUARE:dx+=3.5 +break +case C.OVALBARS:case C.OVAL:dx+=2 +break}} +if(s.notes[m].pit<=yc+3&&dx>xc) +xc=dx} +de.x-=xc;de.y=3*(yc-18)} +function d_trill(de){if(de.ldst) +return +var y,w,tmp,dd=de.dd,de2=de.prev,up=de.start.up,s2=de.s,st=s2.st,s=de.start.s,x=s.x +function sh_st(){var de3,de2=de.start,s=de2.s,i=de2.ix +while(--i>=0){de3=a_de[i] +if(!de3||de3.s!=s) +break} +while(1){i++ +de3=a_de[i] +if(!de3||de3.s!=s) +break +if(de3==de2) +continue +if(!(up^de3.up)&&(de3.dd.name=="trill"||de3.dd.func==6)){x+=de3.dd.wr+2 +break}}} +function sh_en(){var de3,i=de.ix +while(--i>0){de3=a_de[i] +if(!de3||de3.s!=s2) +break} +while(1){i++ +de3=a_de[i] +if(!de3||de3.s!=s2) +break +if(de3==de) +continue +if(!(up^de3.up)&&de3.dd.func==6){w-=de3.dd.wl +break}}} +if(de2){x=de2.s.x+de.dd.wl+2 +de2.val-=de2.dd.wr +if(de2.val<8) +de2.val=8} +de.st=st +de.up=up +sh_st() +if(de.defl.noen){w=de.x-x +if(w<20){x=de.x-20-3;w=20}}else{w=s2.x-x-4 +sh_en(de) +if(w<20) +w=20} +y=y_get(st,up,x-dd.wl,w) +if(up){tmp=staff_tb[s.st].topbar+2 +if(ytmp) +y=tmp +y-=dd.h} +if(de2){if(up){if(y=de2.y){y=de2.y}else{do{de2.y=y +de2=de2.prev}while(de2)}}} +de.lden=false;de.has_val=true;de.val=w;de.x=x;de.y=y +if(up) +y+=dd.h;else +y-=dd.hd +y_set(st,up,x,w,y) +if(up) +s.ymx=s2.ymx=y +else +s.ymn=s2.ymn=y} +function d_upstaff(de){if(de.ldst) +return +if(de.start){d_trill(de) +return} +var y,inv,up=de.up,s=de.s,dd=de.dd,x=de.x,w=dd.wl+dd.wr +switch(dd.glyph){case"brth":case"caes":case"lphr":case"mphr":case"sphr":case"short":case"tick":y=staff_tb[s.st].topbar+2+dd.hd +if(s.type==C.BAR){s.invis=1}else{if(dd.glyph=="brth"&&y=0?0:s.nhd].shhd;switch(dd.ty){case'@':case'<':case'>':y=de.y +break} +if(y==undefined){if(up){y=y_get(s.st,true,x-dd.wl,w) ++dd.hd +if(de.y>y) +y=de.y +s.ymx=y+dd.h}else{y=y_get(s.st,false,x-dd.wl,w) +-dd.h +if(de.y5&&x>realwidth-dd.wr) +de.x=x=realwidth-dd.wr +if(up) +y_set(s.st,1,x-dd.wl,w,y+dd.h) +else +y_set(s.st,0,x-dd.wl,w,y-dd.hd) +de.y=y} +function deco_add(param){var dv=param.match(/(\S*)\s+(.*)/);decos[dv[1]]=dv[2]} +function deco_def(nm,nmd){if(!nmd) +nmd=nm +var a,dd,dd2,nm2,c,i,elts,str,hd,text=decos[nmd] +if(!text&&/\d[()]$/.test(nmd)) +text=decos[nmd.replace(/\d/,'')] +if(!text){if(cfmt.decoerr) +error(1,null,"Unknown decoration '$1'",nm) +return} +a=text.match(/(\d+)\s+(.+?)\s+([0-9.,]+)\s+([0-9.]+)\s+([0-9.]+)/) +if(!a){error(1,null,"Invalid decoration '$1'",nm) +return} +var c_func=Number(a[1]),h=a[3],wl=parseFloat(a[4]),wr=parseFloat(a[5]) +if(isNaN(c_func)){error(1,null,"%%deco: bad C function value '$1'",a[1]) +return} +if(c_func>10&&(c_func<32||c_func>44)){error(1,null,"%%deco: bad C function index '$1'",c_func) +return} +if(h.indexOf(',')>0){h=h.split(',') +hd=h[1] +h=h[0]}else{hd=0} +if(h>50||wl>80||wr>80){error(1,null,"%%deco: abnormal h/wl/wr value '$1'",text) +return} +dd=dd_tb[nm] +if(!dd){dd={name:nm} +dd_tb[nm]=dd} +dd.func=nm.indexOf("head-")==0?9:c_func;dd.glyph=a[2];dd.h=Number(h) +dd.hd=Number(hd) +dd.wl=wl;dd.wr=wr;str=text.replace(a[0],'').trim() +if(str){if(str[0]=='"') +str=str.slice(1,-1);if(str[0]=='@'){c=str.match(/^@([0-9.-]+),([0-9.-]+);?/) +if(!c){error(1,null,"%%deco: bad position '$1'",str) +return} +dd.dx=+c[1] +dd.dy=+c[2] +str=str.replace(c[0],'')} +dd.str=str} +if(dd.func==6&&dd.str==undefined) +dd.str=nm +c=nm.slice(-1) +if(c=='('||(c==')'&&nm.indexOf('(')<0)){dd.str=null;nm2=nm.slice(0,-1)+(c=='('?')':'(');dd2=dd_tb[nm2] +if(dd2){if(c=='('){dd.dd_en=dd2;dd2.dd_st=dd}else{dd.dd_st=dd2;dd2.dd_en=dd}}} +return dd} +function do_ctie(nm,s,nt1){var nt2=cross[nm],nm2=nm.slice(0,-1)+(nm.slice(-1)=='('?')':'(') +if(nt2){error(1,s,"Conflict on !$1!",nm) +return} +nt1.s=s +nt2=cross[nm2] +if(!nt2){cross[nm]=nt1 +return} +if(nm.slice(-1)==')'){nt2=nt1 +nt1=cross[nm2]} +cross[nm2]=null +if(nt1.midi!=nt2.midi||nt1.s.time+nt1.s.dur!=nt2.s.time){error(1,s,"Bad tie")}else{nt1.tie_ty=C.SL_AUTO +nt1.tie_e=nt2 +nt2.tie_s=nt1 +nt1.s.ti1=nt2.s.ti2=true}} +function get_dd(nm){var ty,p,dd=dd_tb[nm] +if(dd) +return dd +if("<>^_@".indexOf(nm[0])>=0&&!/^([>^]|[<>]\d?[()])$/.test(nm)){ty=nm[0] +if(ty=='@'){p=nm.match(/@([-\d]+),([-\d]+)/) +if(p) +ty=p[0] +else +ty=''} +dd=deco_def(nm,nm.replace(ty,''))}else{dd=deco_def(nm)} +if(!dd) +return +if(ty){if(ty[0]=='@'){dd.x=Number(p[1]) +dd.y=Number(p[2]) +ty='@'} +dd.ty=ty} +return dd} +function deco_cnv(s,prev){var i,j,dd,nm,note,s1,court +while(1){nm=a_dcn.shift() +if(!nm) +break +dd=get_dd(nm) +if(!dd) +continue +switch(dd.func){case 0:if(s.type==C.BAR&&nm=="dot"){s.bar_dotted=true +continue} +case 1:case 2:if(!s.notes){error(1,s,errs.must_note_rest,nm) +continue} +break +case 4:case 5:i=nm.match(/1?[85]([vm])([ab])([()])/) +if(i){j=i[1]=='v'?1:2 +if(i[2]=='b') +j=-j +if(!s.ottava) +s.ottava=[] +s.ottava[i[3]=='('?0:1]=j +glovar.ottava=1} +break +case 8:if(s.type!=C.NOTE){error(1,s,errs.must_note,nm) +continue} +note=s.notes[s.nhd] +if(!note.a_dd) +note.a_dd=[] +note.a_dd.push(dd) +continue +case 9:if(!s.notes){error(1,s,errs.must_note_rest,nm) +continue} +for(j=0;j<=s.nhd;j++){note=s.notes[j] +note.invis=true +if(!note.a_dd) +note.a_dd=[] +note.a_dd.push(dd)} +continue +case 10:if(s.notes){for(j=0;j<=s.nhd;j++) +s.notes[j].color=nm}else{s.color=nm} +break +case 32:s.invis=true +break +case 33:if(s.type!=C.BAR){error(1,s,"!beamon! must be on a bar") +continue} +s.beam_on=true +break +case 34:if(s.type!=C.NOTE||!prev||prev.type!=C.NOTE||s.dur!=prev.dur){error(1,s,"!$1! must be on the last of a couple of notes",nm) +continue} +s.trem2=true;s.beam_end=true;s.beam_st=false;prev.beam_st=true;prev.beam_end=false;s.ntrem=prev.ntrem=Number(nm[4]);for(j=0;j<=s.nhd;j++) +s.notes[j].dur*=2;for(j=0;j<=prev.nhd;j++) +prev.notes[j].dur*=2 +break +case 35:if(s.type!=C.NOTE){error(1,s,errs.must_note,nm) +continue} +s.xstem=true;break +case 36:if(s.type!=C.NOTE){error(1,s,errs.must_note,nm) +continue} +if(nm[6]=='1') +s.beam_br1=true +else +s.beam_br2=true +break +case 37:s.rbstop=1 +break +case 38:if(s.type!=C.NOTE){error(1,s,errs.must_note,nm) +continue} +s.trem1=true;s.ntrem=nm.length +break +case 39:if(s.type!=C.NOTE){error(1,s,errs.must_note,nm) +continue} +s.feathered_beam=nm[5]=='a'?1:-1;break +case 40:s.stemless=true +break +case 41:s.rbstop=2 +break +case 42:if(s.type!=C.NOTE){error(1,s,errs.must_note,nm) +continue} +if(!s.notes[0].acc) +continue +nm="sacc"+s.notes[0].acc.toString() +dd=dd_tb[nm] +if(!dd){dd=deco_def(nm) +if(!dd){error(1,s,errs.bad_val,"!editorial!") +continue}} +delete s.notes[0].acc +curvoice.acc[s.notes[0].pit+19]=0 +break +case 43:if(s.type!=C.NOTE){error(1,s,errs.must_note,nm) +continue} +j=curvoice.acc[s.notes[0].pit+19] +if(s.notes[0].acc||!j) +continue +court=1 +break +case 44:if(s.type!=C.NOTE){error(1,s,errs.must_note,nm) +continue} +do_ctie(nm,s,s.notes[0]) +continue} +if(!s.a_dd) +s.a_dd=[] +s.a_dd.push(dd)} +if(court){a_dcn.push("cacc"+j) +dh_cnv(s,s.notes[0])}} +function dh_cnv(s,nt){var k,nm,dd +while(1){nm=a_dcn.shift() +if(!nm) +break +dd=get_dd(nm) +if(!dd) +continue +switch(dd.func){case 0:case 1:case 3:case 4:case 8:break +default:error(1,s,"Cannot have !$1! on a head",nm) +continue +case 9:nt.invis=true +break +case 32:nt.invis=true +continue +case 10:nt.color=nm +continue +case 40:s.stemless=true +continue +case 44:do_ctie(nm,s,nt) +continue} +if(!nt.a_dd) +nt.a_dd=[] +nt.a_dd.push(dd)}} +function deco_update(s,dx){var i,de,nd=a_de.length +for(i=0;i':w=wr+dd.wl+dd.wr+6 +if(s.wrwl) +wl=w}} +return wl} +Abc.prototype.draw_all_deco=function(){if(!a_de.length) +return +var de,dd,s,note,f,st,x,y,y2,ym,uf,i,str,a,new_de=[],ymid=[] +st=nstaff;y=staff_tb[st].y +while(--st>=0){y2=staff_tb[st].y;ymid[st]=(y+24+y2)*.5;y=y2} +while(1){de=a_de.shift() +if(!de) +break +dd=de.dd +if(!dd) +continue +if(dd.dd_en) +continue +s=de.s +f=dd.glyph;i=f.indexOf('/') +if(i>0){if(s.stem>=0) +f=f.slice(0,i) +else +f=f.slice(i+1)} +if(f_staff[dd.func]) +set_sscale(s.st) +else +set_scale(s);st=de.st;if(!staff_tb[st].topbar) +continue +x=de.x+(dd.dx||0) +y=de.y+staff_tb[st].y+(dd.dy||0) +if(de.m!=undefined){note=s.notes[de.m];if(note.shhd) +x+=note.shhd*stv_g.scale}else if(dd.func==6&&((de.pos&C.SL_ALI_MSK)==C.SL_CENTER||((de.pos&C.SL_ALI_MSK)==0&&!s.fmt.dynalign))&&((de.up&&st>0)||(!de.up&&stym)){y2=y_get(st,!de.up,de.x,de.val) ++staff_tb[st].y +if(de.up) +y2-=dd.h +if((de.up&&y2>ym)||(!de.up&&y2=10) +continue +pos=0 +break +case 3:case 4:case 5:pos=s.pos.orn +break +case 6:pos=s.pos.dyn +break} +switch(dd.ty){case'^':pos=(pos&~0x07)|C.SL_ABOVE +break +case'_':pos=(pos&~0x07)|C.SL_BELOW +break +case'<':case'>':pos=(pos&0x07)|C.SL_CLOSE +if(dd.ty=='<'){x-=dd.wr+8 +if(s.notes[0].acc) +x-=8}else{x+=dd.wl+8} +y=3*(s.notes[0].pit-18) +-(dd.h-dd.hd)/2 +break +case'@':x+=dd.x +y+=dd.y +break} +if((pos&0x07)==C.SL_HIDDEN) +continue +de={s:s,dd:dd,st:s.st,ix:a_de.length,defl:{},x:x,y:y} +if(pos) +de.pos=pos +up=0 +if(dd.ty=='^'){up=1}else if(dd.ty=='_'){}else{switch(dd.func){case 0:if(s.multi) +up=s.multi>0 +else +up=s.stem<0 +break +case 3:case 5:up=up3(s,pos) +break +case 6:case 7:up=up6(s,pos) +break}} +de.up=up +if(dd.name.indexOf("inverted")>=0) +de.inv=1 +if(s.type==C.BAR&&!dd.ty) +de.x-=s.wl/2-2 +a_de.push(de) +if(dd.dd_en){de.ldst=true}else if(dd.dd_st){de.lden=true;de.defl.nost=true} +if(f_near[dd.func]) +f_near[dd.func](de)}} +function create_dh(s,m){var de,k,dd,note=s.notes[m],nd=note.a_dd.length,x=s.x +for(k=0;k'){de.x+=dd.wl+8}}} +a_de.push(de) +if(dd.dd_en){de.ldst=true}else if(dd.dd_st){de.lden=true;de.defl.nost=true}}} +function create_all(s){if(s.invis&&s.play) +return +if(s.a_dd) +create_deco(s) +if(s.notes){for(var m=0;m=0){de3=a_de[j] +if(!de3.start) +continue +if(de3.s.time0&s.bar_num&&s.bar_num%cfmt.measurenb) +x+=6 +if(s.type!=C.BAR){w=s.rbstop?0:s.x-realwidth+4}else if((s.bar_type.length>1&&s.bar_type!="[]")||s.bar_type=="]"){if(s1.st>0&&!(cur_sy.staves[s1.st-1].flags&STOP_BAR)) +w=s.wl +else if(s.bar_type.slice(-1)==':') +w=12 +else if(s.bar_type[0]!=':') +w=0 +else +w=8}else{w=(s.rbstop&&!s.rbstart)?0:8} +w=(s.x-x-w) +if(!s.next&&!s.rbstop&&!p_voice.bar_start){p_voice.bar_start=_bar(s) +p_voice.bar_start.bar_type="" +p_voice.bar_start.rbstart=1} +if(s1.text) +xy_str(x+4,y2-gene.curfont.size,s1.text);xypath(x,y2);if(s1.rbstart==2) +output+='m0 10v-10';output+='h'+w.toFixed(1) +if(s.rbstop==2) +output+='v10';output+='"/>\n';y_set(s1.st,true,x,w,y+2) +if(s.rbstart) +s=s.prev}} +for(i=0;i<=nstaff;i++) +minmax[i]={ymin:0,ymax:0} +for(i=0;i'||dd.ty=='@') +continue +f_staff[dd.func](de) +if(dd.func!=6||dd.dd_en) +continue +if((de.pos&C.SL_ALI_MSK)==C.SL_ALIGN||((de.pos&C.SL_ALI_MSK)==0&&de.s.fmt.dynalign>0)){if(de.up){if(de.y>minmax[de.st].ymax) +minmax[de.st].ymax=de.y}else{if(de.y0){y2=y+dd.h+2 +if(y2>staff_tb[de.st].ann_top) +staff_tb[de.st].ann_top=y2}else{y2=y-dd.hd-2 +if(y2'||dd.dd_en) +continue +w=de.val||(dd.wl+dd.wr) +if((de.pos&C.SL_ALI_MSK)==C.SL_ALIGN||((de.pos&C.SL_ALI_MSK)==0&&de.s.fmt.dynalign>0)){if(de.up) +y=minmax[de.st].ymax +else +y=minmax[de.st].ymin;de.y=y}else{y=de.y} +if(de.up) +y+=dd.h;else +y-=dd.hd +y_set(de.st,de.up,de.x,w,y)} +for(i=0;instaff) +return +set_dscale(st) +if(staff_tb[st].staffscale!=1){font_size=get_font("measure").size;param_set_font("measurefont","* "+ +(font_size/staff_tb[st].staffscale).toString())} +set_font("measure");w0=cwidf('0');s=tsfirst;bar_num=gene.nbar +if(bar_num>1){if(cfmt.measurenb==0){any_nb=true;y=y_get(st,true,0,20) +if(y=10) +w*=bar_num>=100?3:2 +if(gene.curfont.pad) +w+=gene.curfont.pad*2 +x=(s.prev?s.prev.x+s.prev.wr/2:s.x-s.wl)-w +y=y_get(st,true,x,w)+5 +if(y=10) +w*=bar_num>=100?3:2 +if(gene.curfont.pad) +w+=gene.curfont.pad*2 +x=s.x +y=y_get(st,true,x,w) +if(y0){if(ynstaff) +return +set_dscale(st,1) +var ymin=staff_tb[st].topbar+2,dosh=0,shift=1,x=-100,yn=0 +for(s=tsfirst;s;s=s.ts_next){s2=s.part +if(!s2||s2.invis) +continue +if(!some_part){some_part=s;set_font("parts");h=gene.curfont.size+2+ +gene.curfont.pad*2} +if(s2.x==undefined) +s2.x=s.x-10 +w=strwh(s2.text)[0] +y=y_get(st,true,s2.x,w+3) +if(yminymin) +ymin=y +if(x>=s.x-16&&!(dosh&(shift>>1))) +dosh|=shift +shift<<=1 +x=s.x-16+w} +if(some_tempo){set_sscale(-1) +set_font("tempo") +h=gene.curfont.size +ymin+=2 +ymin*=staff_tb[st].staffscale +for(s=some_tempo;s;s=s.ts_next){if(s.type!=C.TEMPO||s.invis) +continue +w=s.tempo_wh[0] +y=ymin +if(dosh&1) +y+=h +if(user.anno_start||user.anno_stop){s.wl=16 +s.wr=w-16 +s.ymn=y +s.ymx=s.ymn+14 +anno_start(s)} +writempo(s,s.x-16,y) +anno_stop(s) +y_set(st,1,s.x-16,w,y+h+2) +dosh>>=1}}} +var STEM_MIN=16,STEM_MIN2=14,STEM_MIN3=12,STEM_MIN4=10,STEM_CH_MIN=14,STEM_CH_MIN2=10,STEM_CH_MIN3=9,STEM_CH_MIN4=9,BEAM_DEPTH=3.2,BEAM_OFFSET=.25,BEAM_SHIFT=5,BEAM_STUB=7,SLUR_SLOPE=.5,GSTEM=15,GSTEM_XOFF=2.3 +var cache,anno_a=[] +function b_pos(grace,stem,nflags,b){var top,bot,d1,d2,shift=!grace?BEAM_SHIFT:3.5,depth=!grace?BEAM_DEPTH:1.8 +function rnd6(y){var iy=Math.round((y+12)/6)*6-12 +return iy-y} +if(stem>0){bot=b-(nflags-1)*shift-depth +if(bot>26) +return 0 +top=b}else{top=b+(nflags-1)*shift+depth +if(top<-2) +return 0 +bot=b} +d1=rnd6(top-BEAM_OFFSET);d2=rnd6(bot+BEAM_OFFSET) +return d1*d1>d2*d2?d2:d1} +function sym_dup(s){var m,note +s=clone(s) +s.invis=true +delete s.extra;delete s.text +delete s.a_gch +delete s.a_ly +delete s.a_dd;delete s.tp +s.notes=clone(s.notes) +for(m=0;m<=s.nhd;m++){note=s.notes[m]=clone(s.notes[m]) +delete note.a_dd} +return s} +var min_tb=[[STEM_MIN,STEM_MIN,STEM_MIN2,STEM_MIN3,STEM_MIN4,STEM_MIN4],[STEM_CH_MIN,STEM_CH_MIN,STEM_CH_MIN2,STEM_CH_MIN3,STEM_CH_MIN4,STEM_CH_MIN4]] +Abc.prototype.calculate_beam=function(bm,s1){var s,s2,g,notes,nflags,st,v,two_staves,two_dir,x,y,ys,a,b,stem_err,max_stem_err,p_min,p_max,s_closest,stem_xoff,scale,visible,dy +if(!s1.beam_st){s=sym_dup(s1);lkvsym(s,s1);lktsym(s,s1);s.x-=12 +if(s.x>s1.prev.x+12) +s.x=s1.prev.x+12;s.beam_st=true +delete s.beam_end;s.tmp=true +delete s.sls;s1=s} +notes=nflags=0;two_staves=two_dir=false;st=s1.st;v=s1.v;stem_xoff=s1.grace?GSTEM_XOFF:3.5 +for(s2=s1;;s2=s2.next){if(s2.type==C.NOTE){if(s2.nflags>nflags) +nflags=s2.nflags;notes++ +if(s2.st!=st) +two_staves=true +if(s2.stem!=s1.stem) +two_dir=true +if(!visible&&!s2.invis&&(!s2.stemless||s2.trem2)) +visible=true +if(s2.beam_end) +break} +if(!s2.next){for(;;s2=s2.prev){if(s2.type==C.NOTE) +break} +s=sym_dup(s2);s.next=s2.next +if(s.next) +s.next.prev=s;s2.next=s;s.prev=s2;s.ts_next=s2.ts_next +if(s.ts_next) +s.ts_next.ts_prev=s;s2.ts_next=s;s.ts_prev=s2 +delete s.beam_st;s.beam_end=true;s.tmp=true +delete s.sls;s.x+=12 +if(s.x=0){x=stem_xoff+s.notes[0].shhd +if(s.notes[s.nhd].pit>p_max){p_max=s.notes[s.nhd].pit;s_closest=s}}else{x=-stem_xoff+s.notes[s.nhd].shhd +if(s.notes[0].pit=3&&s_closest!=s1&&s_closest!=s2) +a=0 +y=s1.ys+staff_tb[st].y +if(a==undefined) +a=(s2.ys+staff_tb[s2.st].y-y)/(s2.xs-s1.xs) +if(a!=0){a=s1.fmt.beamslope*a/(s1.fmt.beamslope+Math.abs(a)) +if(a>-.04&&a<.04) +a=0} +b=(y+s2.ys+staff_tb[s2.st].y)/2-a*(s2.xs+s1.xs)/2 +max_stem_err=0;s=s1 +if(two_dir){ys=((s1.grace?3.5:BEAM_SHIFT)*(nflags-1)+ +BEAM_DEPTH)*.5 +if(s1.nflags==s2.nflags);else if(s1.stem!=s2.stem&&s1.nflagss1.xs) +s=s.ts_prev +for(;s&&s.time<=s2.time;s=s.ts_next){if(s.type!=C.NOTE||s.invis||(s.st!=st&&s.v!=v)){continue} +x=s.v==v?s.xs:s.x;ys=a*x+b-staff_tb[s.st].y +if(s.v==v){stem_err=min_tb[s.nhd==0?0:1][s.nflags] +if(s.stem>0){if(s.notes[s.nhd].pit>26){stem_err-=2 +if(s.notes[s.nhd].pit>28) +stem_err-=2} +stem_err-=ys-3*(s.notes[s.nhd].pit-18)}else{if(s.notes[0].pit<18){stem_err-=2 +if(s.notes[0].pit<16) +stem_err-=2} +stem_err-=3*(s.notes[0].pit-18)-ys} +stem_err+=BEAM_DEPTH+BEAM_SHIFT*(s.nflags-1)}else{if(s1.stem>0){if(s.stem>0){if(s.ymn>ys+4||s.ymxv) +stem_err=s.ymx-ys +else +stem_err=s.ymn+8-ys}else{stem_err=s.ymx-ys}}else{if(s.stem<0){if(s.ymxys-beam_h-2) +continue +if(s.vmax_stem_err) +max_stem_err=stem_err}}else{for(;;s=s.next){ys=a*s.xs+b-staff_tb[s.st].y;stem_err=GSTEM-2 +if(s.stem>0) +stem_err-=ys-(3*(s.notes[s.nhd].pit-18)) +else +stem_err+=ys-(3*(s.notes[0].pit-18));stem_err+=3*(s.nflags-1) +if(stem_err>max_stem_err) +max_stem_err=stem_err +if(s==s2) +break}} +if(max_stem_err>0) +b+=s1.stem*max_stem_err +if(!two_staves&&!two_dir) +for(s=s1.next;;s=s.next){switch(s.type){case C.REST:if(!s.multi) +break +g=s.ts_next +if(!g||g.st!=st||(g.type!=C.NOTE&&g.type!=C.REST)) +break +if(s.invis) +break +case C.CLEF:y=a*s.x+b +if(s1.stem>0){y=s.ymx-y ++BEAM_DEPTH+BEAM_SHIFT*(nflags-1) ++2 +if(y>0) +b+=y}else{y=s.ymn-y +-BEAM_DEPTH-BEAM_SHIFT*(nflags-1) +-2 +if(y<0) +b+=y} +break +case C.GRACE:for(g=s.extra;g;g=g.next){y=a*g.x+b +if(s1.stem>0){y=g.ymx-y ++BEAM_DEPTH+BEAM_SHIFT*(nflags-1) ++2 +if(y>0) +b+=y}else{y=g.ymn-y +-BEAM_DEPTH-BEAM_SHIFT*(nflags-1) +-2 +if(y<0) +b+=y}} +break} +if(s==s2) +break} +if(a==0) +b+=b_pos(s1.grace,s1.stem,nflags,b-staff_tb[st].y) +for(s=s1;;s=s.next){switch(s.type){case C.NOTE:s.ys=a*s.xs+b-staff_tb[s.st].y +if(s.stem>0){s.ymx=s.ys+2.5 +if(s.ts_prev&&s.ts_prev.stem>0&&s.ts_prev.st==s.st&&s.ts_prev.ymn0){y-=dy +if(s1.multi==0&&y>12) +y=12 +if(s.y<=y) +break}else{y+=dy +if(s1.multi==0&&y<12) +y=12 +if(s.y>=y) +break} +if(s.head!=C.FULL) +y=(((y+3+12)/6)|0)*6-12;s.y=y +break} +if(s==s2) +break} +if(staff_tb[st].y==0) +return false +bm.s1=s1;bm.a=a;bm.b=b;bm.nflags=nflags +return true} +function draw_beams(bm){var s,i,beam_dir,shift,bshift,bstub,bh,da,bd,k,k1,k2,x1,s1=bm.s1,s2=bm.s2 +function draw_beam(x1,x2,dy,h,bm,n){var y1,dy2,s=bm.s1,nflags=s.nflags +if(s.ntrem) +nflags-=s.ntrem +if(s.trem2&&n>nflags){if(s.dur>=C.BLEN/2){x1=s.x+6;x2=bm.s2.x-6}else if(s.dur\n'} +anno_start(s1,'beam') +if(!s1.grace){bshift=BEAM_SHIFT;bstub=BEAM_STUB;shift=.34;bh=BEAM_DEPTH}else{bshift=3.5;bstub=3.2;shift=.29;bh=1.8} +bh/=stv_g.scale +beam_dir=s1.stem +if(s1.stem!=s2.stem&&s1.nflags0){da=-da;bshift=da*s1.xs}else{bshift=da*s2.xs} +da=da*beam_dir} +shift=0 +for(i=2;i<=bm.nflags;i++){shift+=bshift +if(da!=0) +bm.a+=da +for(s=s1;;s=s.next){if(s.type!=C.NOTE||s.nflagss.nflags-s.ntrem){x1=(s.dur>=C.BLEN/2)?s.x:s.xs;draw_beam(x1-5,x1+5,(shift+2.5)*beam_dir,bh,bm,i) +if(s==s2) +break +continue} +k1=s +while(1){if(s==s2) +break +k=s.next +if(k.type==C.NOTE||k.type==C.REST){if(k.trem1){if(k.nflags-k.ntrem2)) +break +s=k} +k2=s +while(k2.type!=C.NOTE) +k2=k2.prev;x1=k1.xs +bd=beam_dir +if(k1==k2){if(k1==s1){x1+=bstub}else if(k1==s2){x1-=bstub}else if(k1.beam_br1||(k1.beam_br2&&i>2)){x1+=bstub}else{k=k1.next +while(k.type!=C.NOTE) +k=k.next +if(k.beam_br1||(k.beam_br2&&i>2)){x1-=bstub}else{k1=k1.prev +while(k1.type!=C.NOTE) +k1=k1.prev +if(k1.nflagsi;j--){if(cur_sy.st_print[j]) +break} +if(i==j&&l==0) +return +yb=staff_tb[j].y+staff_tb[j].botbar*staff_tb[j].staffscale;h=staff_tb[i].y+staff_tb[i].topbar*staff_tb[i].staffscale-yb;xypath(x,yb);output+="v"+(-h).toFixed(1)+'"/>\n' +for(i=0;i<=nst;i++){fl=cur_sy.staves[i].flags +if(fl&OPEN_BRACE) +draw_sysbra(x,i,CLOSE_BRACE) +if(fl&OPEN_BRACKET) +draw_sysbra(x,i,CLOSE_BRACKET) +if(fl&OPEN_BRACE2) +draw_sysbra(x-6,i,CLOSE_BRACE2) +if(fl&OPEN_BRACKET2) +draw_sysbra(x-6,i,CLOSE_BRACKET2)}} +function draw_meter(s){if(!s.a_meter) +return +var dx,i,j,meter,x,st=s.st,p_staff=staff_tb[st],y=p_staff.y;if(p_staff.stafflines!='|||||') +y+=(p_staff.topbar+p_staff.botbar)/2-12 +for(i=0;i\n\ + A\n\ + B\n\ +\n',x,y+6,m_gl(meter.top),m_gl(meter.bot))}else{out_XYAB('\ +A\n',x,y+12,m_gl(meter.top))}}} +var acc_nd={} +function draw_acc(x,y,a){if(typeof a=="object"){var c,n=a[0],d=a[1] +c=n+'_'+d +a=acc_nd[c] +if(!a){a=abc2svg.rat(Math.abs(n),d) +d=a[1] +a=(n<0?-a[0]:a[0]).toString() +if(d!=1) +a+='_'+d +acc_nd[c]=a}} +xygl(x,y,"acc"+a)} +function set_hl(p_st,n,x,dx1,dx2){var i,hl +if(n>=0){hl=p_st.hlu[n] +if(!hl) +hl=p_st.hlu[n]=[]}else{hl=p_st.hld[-n] +if(!hl) +hl=p_st.hld[-n]=[]} +for(i=0;i=hl[i][0]) +break} +if(i==hl.length){hl.push([x,dx1,dx2])}else if(x>hl[i][0]){hl.splice(++i,0,[x,dx1,dx2])}else{if(dx1hl[i][2]) +hl[i][2]=dx2}} +Abc.prototype.draw_hl=function(s){var i,j,n,note,hla=[],st=s.st,p_staff=staff_tb[st] +if(!p_staff.hll||s.invis) +return +for(i=0;i<=s.nhd;i++){note=s.notes[i] +if(!p_staff.hlmap[note.pit-p_staff.hll]) +hla.push([note.pit-18,note.shhd*stv_g.scale])} +n=hla.length +if(!n) +return +var dx1,dx2,hl,shhd,hlp,stafflines=p_staff.stafflines,top=stafflines.length-1,yu=top,bot=p_staff.botline/6,yl=bot,dx=s.grace?4:hw_tb[s.head]*1.3 +note=s.notes[s.stem<0?s.nhd:0] +shhd=note.shhd +for(i=0;ishhd?hla[i][1]:shhd)+dx +if(hlp>1 +n--}else if(hlp>top*2){yu=hlp>>1 +n--} +set_hl(p_staff,hlp>>1,s.x,dx1,dx2)} +dx1=shhd-dx +dx2=shhd+dx +while(++yltop) +set_hl(p_staff,yu,s.x,dx1,dx2) +if(!n) +return +i=yl;j=yu +while(i>bot&&stafflines[i]=='-') +i-- +while(j0){for(nacc=0;nacc9?sharp1:sharp2 +for(i=0;iold_sf;i--){xygl(x,staffb+shift,"acc3");shift+=p_seq[-i];x+=5.5} +if(s.k_sf!=0) +x+=3}} +if(s.k_sf>0){shift=sharp_cl[clef_ix];p_seq=shift>9?sharp1:sharp2 +for(i=0;is.k_sf;i--){xygl(x,staffb+shift,"acc-1");shift+=p_seq[-i];x+=5.5} +if(s.fmt.cancelkey&&i>old_sf){x+=2 +for(;i>old_sf;i--){xygl(x,staffb+shift,"acc3");shift+=p_seq[-i];x+=5.5}}}}else if(a_acc.length){var acc,last_acc=a_acc[0].acc,last_shift=100,s2={st:st,nhd:0,notes:[{}]} +for(i=0;i27) +shift-=21 +if(i!=0&&(shift>last_shift+18||shift2&&s.v==cur_sy.top_voice&&s.fmt.measrepnb>0&&!(s.rep_nb%s.fmt.measrepnb)) +nrep_out(x,yb+p_staff.topbar,s.rep_nb)} +anno_a.push(s) +return} +set_scale(s);anno_start(s);if(s.notes[0].color) +set_color(s.notes[0].color);y=s.y;i=5-s.nflags +if(i==7&&y==12&&p_staff.stafflines.length<=2) +y-=6 +if(!s.notes[0].invis) +xygl(x,y+yb,rest_tb[i]) +if(s.dots){x+=8;y+=yb+3 +j=s.dots +i=(s.dur_orig/12)>>((5-s.nflags)-j) +while(j-->0){xygl(x,y,(i&(1<>2 +if(n&3){k++ +if(n&3==3) +k++} +x-=3*(k-1) +while(n>=4){xygl(x,y,"r00") +n-=4 +x+=6} +if(n>=2){xygl(x,y,"r0") +n-=2 +x+=6} +if(n) +xygl(x+2,y,"r1")} +if(!s.next){error(1,s,"Lack of bar after multi-measure rest") +return} +set_scale(s) +prev=s +while(!prev.seqst) +prev=prev.ts_prev +prev=prev.ts_prev +while(!prev.seqst) +prev=prev.ts_prev +x1=prev.x+20 +x2=s.next.x-20 +s.x=(x1+x2)/2 +anno_start(s) +if(!cfmt.oldmrest||s.nmes>cfmt.oldmrest){out_XYAB('\n'}else{omrest()} +if(s.tacet) +out_XYAB('A\n',s.x,y+18,s.tacet) +else +out_XYAB('A\n',s.x,y+22,m_gl(p)) +anno_a.push(s)} +function grace_slur(s){var yy,x0,y0,x3,y3,bet1,bet2,dy1,dy2,last,below,so=s,g=s.extra +while(1){if(!g.next) +break +g=g.next} +last=g +below=((g.stem>=0||s.multi<0)&&g.notes[0].pit<=28)||g.notes[0].pit<16 +if(below){yy=127 +for(g=s.extra;g;g=g.next){if(g.y-2) +x3-=4;y3=3*(s.notes[0].pit-18)-5;dy1=(x3-x0)*.4 +if(dy1>3) +dy1=3;dy2=dy1;bet1=.2;bet2=.8 +if(y0>y3+7){x0=last.x-1;y0+=.5;y3+=6.5;x3=s.x-5.5;dy1=(y0-y3)*.8;dy2=(y0-y3)*.2;bet1=0}else if(y3>y0+4){y3=y0+4;x0=last.x+2;y0=last.y-4}}else{yy=-127 +for(g=s.extra;g;g=g.next){if(g.y>yy){yy=g.y;last=g}} +x0=last.x;y0=last.y+5 +if(s.extra!=last){x0-=4;y0-=1} +s=s.next;x3=s.x-1 +if(s.stem>=0&&s.nflags>-2) +x3-=2;y3=3*(s.notes[s.nhd].pit-18)+5;dy1=(x0-x3)*.4 +if(dy1<-3) +dy1=-3;dy2=dy1;bet1=.2;bet2=.8 +if(y0g.ymx) +g.ymx=y0}} +function draw_gracenotes(s){var x1,y1,last,note,bm={},g=s.extra +while(1){if(g.beam_st&&!g.beam_end){if(self.calculate_beam(bm,g)) +draw_beams(bm)} +anno_start(g) +draw_note(g,!bm.s2) +if(g==bm.s2) +bm.s2=null +anno_a.push(s) +if(!g.next) +break +g=g.next} +last=g +if(s.sappo){g=s.extra +if(!g.next){x1=9 +y1=g.stem>0?5:-5}else{x1=(g.next.x-g.x)*.5+4 +y1=(g.ys+g.next.ys)*.5-g.y +if(g.stem>0) +y1-=1 +else +y1+=1} +note=g.notes[g.stem<0?0:g.nhd] +out_acciac(x_head(g,note),y_head(g,note),x1,y1,g.stem>0)} +g=s.slur +if(g){anno_start(s,'slur') +xypath(g.x0,g.y0+staff_tb[s.st].y) +output+='c'+g.x1.toFixed(1)+' '+g.y1.toFixed(1)+' '+g.x2.toFixed(1)+' '+g.y2.toFixed(1)+' '+g.x3.toFixed(1)+' '+g.y3.toFixed(1)+'"/>\n' +anno_stop(s,'slur')}} +function setdoty(s,y_tb){var m,m1,y +for(m=0;m<=s.nhd;m++){y=3*(s.notes[m].pit-18) +if((y%6)==0){if(s.dot_low) +y-=3 +else +y+=3} +y_tb[m]=y} +for(m=0;my_tb[m]) +continue +m1=m +while(m1>0){if(y_tb[m1]>y_tb[m1-1]+6) +break +m1--} +if(3*(s.notes[m1].pit-18)-y_tb[m1]=0){if(s.stem>=0) +p=p.slice(0,i) +else +p=p.slice(i+1)}}else if(s.type==C.CUSTOS){p="custos"}else{switch(head){case C.OVAL:p="HD" +break +case C.OVALBARS:if(s.head!=C.SQUARE){p="HDD" +break} +case C.SQUARE:if(nflags>-4){p="breve"}else{p="longa" +inv=s.stem>0} +if(!tsnext&&s.next&&s.next.type==C.BAR&&!s.next.next) +dots=0 +x_note+=1 +break +case C.EMPTY:p="Hd" +break +default:p="hd" +break}} +if(note.color!=undefined) +old_color=set_color(note.color) +if(p){if(inv){g_open(x_note,y_note,0,1,-1);x_note=y_note=0} +if(!self.psxygl(x_note,y_note,p)) +xygl(x_note,y_note,p) +if(inv) +g_close()} +if(dots){dotx=x+(7.7+s.xmx)*stv_g.scale +if(y_tb[m]==undefined){y_tb[m]=3*(s.notes[m].pit-18) +if((s.notes[m].pit&1)==0) +y_tb[m]+=3} +doty=y_tb[m]+staffb +i=(note.dur/12)>>((5-nflags)-dots) +while(dots-->0){xygl(dotx,doty,(i&(1<0){if(s.stem>=0) +slen-=1 +else +slen+=1} +out_stem(x,y,slen,s.grace)}else{out_stem(x,y,slen,s.grace,nflags,s.fmt.straightflags)}}else if(s.xstem){s2=s.ts_prev;slen=(s2.stem>0?s2.y:s2.ys)-s.y;slen+=staff_tb[s2.st].y-staffb;out_stem(x,y,slen)} +if(fl&&s.trem1){var ntrem=s.ntrem||0,x1=x;slen=3*(s.notes[s.stem>0?s.nhd:0].pit-18) +if(s.head==C.FULL||s.head==C.EMPTY){x1+=(s.grace?GSTEM_XOFF:3.5)*s.stem +if(s.stem>0) +slen+=6+5.4*ntrem +else +slen-=6+5.4}else{if(s.stem>0) +slen+=5+5.4*ntrem +else +slen-=5+5.4} +slen/=s.p_v.scale;out_trem(x1,staffb+slen,ntrem)} +for(m=0;m<=s.nhd;m++) +draw_basic_note(s,m,y_tb)} +function prev_scut(s){while(s.prev){s=s.prev +if(s.rbstart) +return s} +s=s.p_v.sym +while(s.type!=C.CLEF) +s=s.ts_prev +if(s.next&&s.next.type==C.KEY) +s=s.next +if(s.next&&s.next.type==C.METER) +return s.next +return s} +function slur_direction(k1,k2){var s,some_upstem,low,dir +function slur_multi(s1,s2){if(s1.multi) +return s1.multi +if(s2.multi) +return s2.multi +return 0} +if(k1.grace&&k1.stem>0) +return-1 +dir=slur_multi(k1,k2) +if(dir) +return dir +for(s=k1;;s=s.next){if(s.type==C.NOTE){if(!s.stemless){if(s.stem<0) +return 1 +some_upstem=true} +if(s.notes[0].pit<22) +low=true} +if(s.time==k2.time) +break} +if(!some_upstem&&!low) +return 1 +return-1} +function slur_out(x1,y1,x2,y2,dir,height,dotted){var dx,dy,dz,alfa=.3,beta=.45;dy=y2-y1 +if(dy<0) +dy=-dy;dx=x2-x1 +if(dx>40.&&dy/dx<.7){alfa=.3+.002*(dx-40.) +if(alfa>.7) +alfa=.7} +var mx=.5*(x1+x2),my=.5*(y1+y2),xx1=mx+alfa*(x1-mx),yy1=my+alfa*(y1-my)+height;xx1=x1+beta*(xx1-x1);yy1=y1+beta*(yy1-y1) +var xx2=mx+alfa*(x2-mx),yy2=my+alfa*(y2-my)+height;xx2=x2+beta*(xx2-x2);yy2=y2+beta*(yy2-y2);dy=2*dir;dz=.2+.001*dx +if(dz>.6) +dz=.6;dz*=dir +dx*=.03 +var scale_y=1 +if(!dotted) +output+='\n'} +function draw_slur(path,sl,recurr){var i,k,g,x1,y1,x2,y2,height,addy,s_st2,a,y,z,h,dx,dy,ty=sl.ty,dir=(ty&0x07)==C.SL_ABOVE?1:-1,n=path.length,i1=0,i2=n-1,not1=sl.nts,k1=path[0],k2=path[i2],nn=1 +set_dscale(k1.st) +for(i=1;ik1.st) +h=-h +for(i=0;ih) +h=a.ymx}}else{for(i1=1;i1h) +h=a.ymx} +for(i1=i;i1s_st2.st?'/':'\\' +if(sl.ty&C.SL_CENTER) +ty=ty+ty +else if(k1.st==k2.st) +ty=ty=='/'?'/\\':'\\/' +else +ty+=dir>0?'+':'-' +var savout=output +output="" +draw_slur(path,sl,1) +gene.a_sl.push([k1,s_st2,ty,output]) +output=savout +return} +x1=k1.x +if(k1.notes&&k1.notes[0].shhd) +x1+=k1.notes[0].shhd;x2=k2.x +if(k2.notes) +x2+=k2.notes[0].shhd +if(not1){y1=3*(not1.pit-18)+2*dir +x1+=3}else{y1=dir>0?k1.ymx+2:k1.ymn-2 +if(k1.type==C.NOTE){if(dir>0){if(k1.stem>0){x1+=5 +if(k1.beam_end&&k1.nflags>=-1&&!k1.in_tuplet){if(k1.nflags>0){x1+=2;y1=k1.ys-3}else{y1=k1.ys-6}}else{y1=k1.ys+3}}else{y1=k1.y+8}}else{if(k1.stem<0){x1-=1 +if(k2.grace){y1=k1.y-8}else if(k1.beam_end&&k1.nflags>=-1&&(!k1.in_tuplet||k1.ys0){x1+=2;y1=k1.ys+3}else{y1=k1.ys+6}}else{y1=k1.ys-3}}else{y1=k1.y-8}}}} +if(sl.nte){y2=3*(sl.nte.pit-18)+2*dir +x2-=3}else{y2=dir>0?k2.ymx+2:k2.ymn-2 +if(k2.type==C.NOTE){if(dir>0){if(k2.stem>0){x2+=1 +if(k2.beam_st&&k2.nflags>=-1&&!k2.in_tuplet) +y2=k2.ys-6 +else +y2=k2.ys+3}else{y2=k2.y+8}}else{if(k2.stem<0){x2-=5 +if(k2.beam_st&&k2.nflags>=-1&&!k2.in_tuplet) +y2=k2.ys+6 +else +y2=k2.ys-3}else{y2=k2.y-8}}}} +if(k1.type!=C.NOTE){y1=y2+1.2*dir;x1=k1.x+k1.wr*.5 +if(x1>x2-12) +x1=x2-12} +if(k2.type!=C.NOTE){if(k1.type==C.NOTE) +y2=y1+1.2*dir +else +y2=y1 +if(k1!=k2) +x2=k2.x-k2.wl*.3} +if(nn>=3){k=path[1] +if(k.type!=C.BAR&&k.x0){y=k.ymx-2 +if(y1y) +y1=y}} +k=path[i2-1] +if(k.type!=C.BAR&&k.x>x2-48){if(dir>0){y=k.ymx-2 +if(y2y) +y2=y}}} +a=(y2-y1)/(x2-x1) +if(a>SLUR_SLOPE||a<-SLUR_SLOPE){a=a>SLUR_SLOPE?SLUR_SLOPE:-SLUR_SLOPE +if(a*dir>0) +y1=y2-a*(x2-x1) +else +y2=y1+a*(x2-x1)} +y=y2-y1 +if(y>8) +y=8 +else if(y<-8) +y=-8 +z=y +if(z<0) +z=-z;dx=.5*z;dy=.3*y +if(y*dir>0){x2-=dx;y2-=dy}else{x1+=dx;y1+=dy} +if(k1.grace) +x1=k1.x-GSTEM_XOFF*.5 +if(k2.grace) +x2=k2.x+GSTEM_XOFF*1.5;h=0;a=(y2-y1)/(x2-x1) +if(k1!=k2&&k1.v==k2.v){addy=y1-a*x1 +for(i=1;i0){y=3*(k.notes[k.nhd].pit-18)+6 +if(yh) +h=y}else{y=3*(k.notes[0].pit-18)-6 +if(y>k.ymn) +y=k.ymn;y-=a*k.x+addy +if(y0){y=3*(g.notes[g.nhd].pit-18)+6 +if(yh) +h=y}else{y=3*(g.notes[0].pit-18)-6 +if(y>g.ymn) +y=g.ymn;y-=a*g.x+addy +if(y3) +height=(.08*(x2-x1)+12)*dir +else +height=(.03*(x2-x1)+8)*dir +if(dir>0){if(height<3*h) +height=3*h +if(height>40) +height=40}else{if(height>3*h) +height=3*h +if(height<-40) +height=-40} +y=y2-y1 +if(y<0) +y=-y +if(dir>0){if(height<.8*y) +height=.8*y}else{if(height>-.8*y) +height=-.8*y} +height*=k1.fmt.slurheight;slur_out(x1,y1,x2,y2,dir,height,ty&C.SL_DOTTED);dx=x2-x1;a=(y2-y1)/dx;addy=y1-a*x1 +if(height>0) +addy+=4*Math.sqrt(height)-2 +else +addy-=4*Math.sqrt(-height)-2 +for(i=0;iy) +k.ymn=y +if(recurr) +continue +if(i==i2-1){dx=x2 +if(sl.nte) +dx-=5}else{dx=k.x+k.wr} +if(i!=0) +x1=k.x +if(!i||i==i2) +y-=height/3 +dx-=x1-k.wl +y_set(k1.st,dir>0,x1-k.wl,dx,y)}} +function draw_slurs(s,last){var gr1,i,m,note,sls,nsls +function draw_sls(s,sl){var k,v,i,dir,s3,path=[],s2=sl.se +if(last&&s2.time>last.time) +return +switch(sl.loc){case'i':s=prev_scut(s) +break +case'o':for(s3=s;s3.ts_next;s3=s3.ts_next);s2=s3 +for(;s3;s3=s3.ts_prev){if(s3.v==s.v){s2=s3 +break} +if(s3.st==s.st) +s2=s3 +if(s3.ts_prev.time!=s2.time) +break} +break} +if(s.p_v.s_next&&s2.time>=tsnext.time){if(s2.time==tsnext.time){if(s2.grace){for(s3=tsnext;s3&&s3.time==s2.time;s3=s3.ts_next){if(s3.type==C.GRACE){s3=null +break}}}else{for(s3=tsnext;s3.time==s2.time;s3=s3.ts_next){if(s3==s2){s3=null +break}}}}else{s3=null} +if(!s3){s.p_v.sls.push(sl);s2=s.p_v.s_next.prev +while(s2.next) +s2=s2.next;sl=Object.create(sl)}} +switch(sl.ty&0x07){case C.SL_ABOVE:dir=1;break +case C.SL_BELOW:dir=-1;break +default:dir=s.v!=s2.v?1:slur_direction(s,s2) +sl.ty&=~0x07 +sl.ty|=dir>0?C.SL_ABOVE:C.SL_BELOW +break} +if(s.v==s2.v){v=s.v}if(!cur_sy.voices[s.v]||!cur_sy.voices[s2.v]){v=s.v>s2.v?s.v:s2.v}else if(dir*(cur_sy.voices[s.v].range<=cur_sy.voices[s2.v].range?1:-1)>0) +v=s.v +else +v=s2.v +if(gr1&&!(s2.grace&&s.v==s2.v&&s.time==s2.time)){do{path.push(s);s=s.next}while(s);s=gr1.next}else{path.push(s);if(s.grace) +s=s.next +else +s=s.ts_next} +if(!s2.grace){while(s){if(s.v==v) +path.push(s) +if(s==s2) +break +s=s.ts_next}}else if(s.grace){while(1){path.push(s) +if(s==s2) +break +s=s.next}}else{k=s2 +while(k.prev) +k=k.prev +while(1){if(s.v==v) +path.push(s) +if(s.extra==k) +break +s=s.ts_next} +s=k +while(1){path.push(s) +if(s==s2) +break +s=s.next}} +for(i=1;istd){std=s2.st} +if(s2.tp) +draw_tuplet(s2) +if(s2.tpe) +break} +if(s2) +s2.tpe-- +if(tp.f[0]==1) +return +if(!s2){error(1,s1,"No end of tuplet in this music line") +return} +dir=tp.f[3] +if(!dir){s3=s1 +while(s3&&!s3.stem) +s3=s3.next +dir=(s3&&s3.stem<0)?C.SL_BELOW:C.SL_ABOVE} +set_dscale(dir==C.SL_ABOVE?stu:std) +if(s1==s2||tp.f[1]==2){nb_only=true}else if(tp.f[1]==1){nb_only=true;draw_slur([s1,s2],{ty:dir})}else{if(tp.f[0]!=2&&s1.type==C.NOTE&&s2.type==C.NOTE){nb_only=true +for(s3=s1;;s3=s3.next){if(s3.type!=C.NOTE&&s3.type!=C.REST){if(s3.type==C.GRACE||s3.type==C.SPACE) +continue +nb_only=false +break} +if(s3==s2) +break +if(s3.beam_end){nb_only=false +break}} +if(nb_only&&!s1.beam_st&&!s1.beam_br1&&!s1.beam_br2){for(s3=s1.prev;s3;s3=s3.prev){if(s3.type==C.NOTE||s3.type==C.REST){if(s3.nflags>=s1.nflags) +nb_only=false +break}}} +if(nb_only&&!s2.beam_end){for(s3=s2.next;s3;s3=s3.next){if(s3.type==C.NOTE||s3.type==C.REST){if(!s3.beam_br1&&!s3.beam_br2&&s3.nflags>=s2.nflags) +nb_only=false +break}}}}} +if(nb_only){if(tp.f[2]==1) +return +set_font("tuplet") +xm=(s2.x+s1.x)/2 +if(dir==C.SL_ABOVE) +ym=y_get(stu,1,xm-4,8) +else +ym=y_get(std,0,xm-4,8)- +gene.curfont.size +if(s1.stem*s2.stem>0){if(s1.stem>0) +xm+=4 +else +xm-=4} +yy=ym+gene.curfont.size*.22 +if(tp.f[2]==0) +xy_str(xm,yy,tp.p.toString(),'c') +else +xy_str(xm,yy,tp.p+':'+tp.q,'c') +for(s3=s1;;s3=s3.next){if(s3.x>=xm) +break} +if(dir==C.SL_ABOVE){ym+=gene.curfont.size +if(s3.ymxym) +s3.ymn=ym;y_set(std,0,xm-3,6,ym)} +return} +x1=s1.x-4 +if(s2.dur>s2.prev.dur){s3=s2.next +if(!s3||s3.time!=s2.time+s2.dur){for(s3=s2.ts_next;s3;s3=s3.ts_next){if(s3.seqst&&s3.time>=s2.time+s2.dur) +break}} +x2=s3?s3.x-s3.wl-5:realwidth-6}else{x2=s2.x+4 +r=s2.stem>=0?0:s2.nhd +if(s2.notes[r].shhd>0) +x2+=s2.notes[r].shhd +if(s2.st==stu&&s2.stem>0) +x2+=3.5} +if(dir==C.SL_ABOVE){if(s1.st>=s2.st){if(s1.stem>0) +x1+=3 +ym=y_get(s1.st,1,x1-4,8) +y1=ym>staff_tb[s1.st].topbar+2?ym:staff_tb[s1.st].topbar+2}else{y1=staff_tb[s1.st].topbar+2} +if(s2.st>=s1.st){ym=y_get(s2.st,1,x2-4,8) +y2=ym>staff_tb[s2.st].topbar+2?ym:staff_tb[s2.st].topbar+2}else{y2=staff_tb[s2.st].topbar+2} +xm=.5*(x1+x2);ym=.5*(y1+y2);a=(y2-y1)/(x2-x1);s0=3*(s2.notes[s2.nhd].pit-s1.notes[s1.nhd].pit)/(x2-x1) +if(s0>0){if(a<0) +a=0 +else if(a>s0) +a=s0}else{if(a>0) +a=0 +else if(ady) +dy=yx-yy +if(s3==s2) +break} +ym+=dy;y1=ym+a*(x1-xm);y2=ym+a*(x2-xm);ym+=6 +for(s3=s1;;s3=s3.next){if(s3.st==stu){yy=ym+(s3.x-xm)*a +if(s3.ymx0){if(a<0) +a=0 +else if(a>s0) +a=s0 +if(a>.35) +a=.35}else{if(a>0) +a=0 +else if(ayy) +s3.ymn=yy;y_set(std,0,s3.x-3,6,yy)} +if(s3==s2) +break}} +if(tp.f[2]==1){out_tubr(x1,y1+4,x2-x1,y2-y1,dir==C.SL_ABOVE);return} +out_tubrn(x1,y1,x2-x1,y2-y1,dir==C.SL_ABOVE,tp.f[2]==0?tp.p.toString():tp.p+':'+tp.q);if(dir==C.SL_ABOVE) +y_set(stu,1,xm-3,6,yy+2) +else +y_set(std,0,xm-3,6,yy)} +function draw_tie(not1,not2,job){var m,x1,s,y,h,time,p=job==2?not1.pit:not2.pit,dir=(not1.tie_ty&0x07)==C.SL_ABOVE?1:-1,s1=not1.s,st=s1.st,s2=not2.s,x2=s2.x,sh=not1.shhd +for(m=0;m0){if(msh) +sh=s1.notes[m+1].shhd}else{if(m>0&&p==s1.notes[m-1].pit+1) +if(s1.notes[m-1].shhd>sh) +sh=s1.notes[m-1].shhd} +x1=s1.x+sh +if(job!=2){for(m=0;m0){if(m0&&p==s2.notes[m-1].pit+1) +if(s2.notes[m-1].shhdx2-20) +x1=x2-20 +break +case 2:x2=s1.next?s1.next.x:realwidth +if(x2!=realwidth) +x2-=(x2-x1)*.4 +if(x220){x1+=3.5 +x2-=3.5}else{x1+=1.5 +x2-=1.5} +if(s1.dots&&!(not1.pit&1)&&((dir>0&&!s1.dot_low)||(dir<0&&s1.dot_low))) +x1+=5 +y=staff_tb[st].y+3*(p-18)+dir +h=(.03*(x2-x1)+16)*dir*s1.fmt.tieheight +slur_out(x1,y,x2,y,dir,h,not1.tie_ty&C.SL_DOTTED)} +function draw_all_ties(p_voice){var s,s1,s2,clef_chg,x,dx,m,not1,not2,tim2=0 +s1=p_voice.sym +set_color(s1.color) +for(;s1;s1=s1.next){if(s1.ti2&&s1.time!=tim2){for(m=0;m<=s1.nhd;m++){not2=s1.notes[m] +not1=not2.tie_s +if(!not1||not1.s.v!=s1.v) +continue +draw_tie(not1,not2,1)}} +if(!s1.ti1) +continue +if(s1.type==C.GRACE){for(s=s1.extra;s;s=s.next){for(m=0;m<=s1.nhd;m++){not1=s.notes[m] +not2=not1.tie_e +if(!not2) +continue +draw_tie(not1,not2) +tim2=not2.s.time}} +continue} +for(m=0;m<=s1.nhd;m++){not1=s1.notes[m] +not2=not1.tie_e +if(!not2){if(not1.tie_ty) +draw_tie(not1,not1,2) +continue} +s2=not2.s +if(tsnext&&s2.time>=tsnext.time){draw_tie(not1,not2,2) +continue} +tim2=s2.time +for(s=s1.ts_next;s!=s2;s=s.ts_next){if(s.st!=s1.st) +continue +if(s.type==C.CLEF){clef_chg=true +break}} +if(clef_chg||s1.st!=s2.st){draw_tie(not1,not2,2) +draw_tie(not1,not2,3) +clef_chg=false}else{draw_tie(not1,not2)}}}} +function draw_sym_near(){var p_voice,p_st,s,v,st,y,g,w,i,st,dx,top,bot,ymn,output_sav=output;function set_yab(s1,s2){var y,k=realwidth/YSTEP,i=(s1.x/k)|0,j=(s2.x/k)|0,a=(s1.ys-s2.ys)/(s1.xs-s2.xs),b=s1.ys-s1.xs*a,p_st=staff_tb[s1.st] +k*=a +if(s1.stem>0){while(i<=j){y=k*i+b +if(p_st.top[i]y) +p_st.bot[i]=y +i++}}} +output="" +YSTEP=Math.ceil(realwidth/2) +for(st=0;st<=nstaff;st++){p_st=staff_tb[st] +p_st.top=new Float32Array(YSTEP) +p_st.bot=new Float32Array(YSTEP) +for(i=0;i0){if(s.stemless){dx=-5;w=10}else if(s.beam_st){dx=3;w=s.beam_end?4:10}else{dx=-8;w=s.beam_end?11:16} +y_set(s.st,true,s.x+dx,w,s.ymx);ymn=s.ymn +if(s.notes[0].acc&&ymn>3*(s.notes[0].pit-18)-9) +ymn=3*(s.notes[0].pit-18)-9 +y_set(s.st,false,s.x-s.wl,s.wl+s.wr,ymn)}else{y_set(s.st,true,s.x-s.wl,s.wl+s.wr,s.ymx);if(s.stemless){dx=-5;w=10}else if(s.beam_st){dx=-6;w=s.beam_end?4:10}else{dx=-8;w=s.beam_end?5:16} +dx+=s.notes[0].shhd;y_set(s.st,false,s.x+dx,w,s.ymn)} +if(s.notes[s.nhd].acc){y=3*(s.notes[s.nhd].pit-18) ++(s.notes[s.nhd].acc==-1?11:10) +y_set(s.st,true,s.x-10,10,y)} +if(s.notes[0].acc){y=3*(s.notes[0].pit-18) +-(s.notes[0].acc==-1?5:10) +y_set(s.st,false,s.x-10,10,y)}} +draw_deco_note() +for(v=0;vp_st.top[i]) +p_st.top[i]=top +if(bot=0) +draw_measnb();set_dscale(-1) +for(v=0;v=0;){if(stl[st]) +break} +if(st<0) +return +for(v=0;vnstaff){st--;p_staff=staff_tb[st]} +p_staff=staff_tb[st] +for(i=0;imaxsep) +dy=maxsep;y+=dy;p_staff.y=-y;while(!gene.st_print[++prev_staff]) +staff_tb[prev_staff].y=-y +while(1){sy_staff_prev=sy.staves[prev_staff] +if(sy_staff_prev) +break +sy=sy.next}} +mbot=0 +for(i=0;ival) +mbot=val} +if(mbot>p_staff.ann_bot) +mbot=p_staff.ann_bot;mbot*=staff_tb[prev_staff].staffscale +for(st=0;st<=nstaff;st++){p_staff=staff_tb[st];dy=p_staff.y +if(p_staff.staffscale!=1){p_staff.scale_str='transform="translate(0,'+ +(posy-dy).toFixed(1)+') '+'scale('+p_staff.staffscale.toFixed(2)+')"'}} +if(mbot==0){for(st=nstaff;st>=0;st--){if(gene.st_print[st]) +break} +if(st<0) +return y} +dy=-mbot;staffsep=fmt.staffsep*.5 +if(dymaxsep) +dy=maxsep;return y+dy} +function draw_systems(indent){var s,s2,st,x,x2,res,sy,xstaff=[],stl=[],bar_bot=[],bar_height=[],ba=[],sb="",thb="" +function bar_set(){var st,staffscale,top,bot,dy=0 +for(st=0;st<=cur_sy.nstaff;st++){if(xstaff[st]<0){bar_bot[st]=bar_height[st]=0 +continue} +staffscale=staff_tb[st].staffscale;top=staff_tb[st].topbar*staffscale;bot=staff_tb[st].botbar*staffscale +if(dy==0) +dy=staff_tb[st].y+top;bar_bot[st]=staff_tb[st].y+bot;bar_height[st]=dy-bar_bot[st];dy=(cur_sy.staves[st].flags&STOP_BAR)?0:bar_bot[st]}} +function draw_staff(st,x1,x2){var w,i,dy,ty,y=0,ln="",stafflines=staff_tb[st].stafflines,l=stafflines.length,il=6*staff_tb[st].staffscale +if(!/[\[|]/.test(stafflines)) +return +w=x2-x1;set_sscale(-1) +if(cache&&cache.st_l==stafflines&&staff_tb[st].staffscale==1&&cache.st_w==(w|0)){xygl(x1,staff_tb[st].y,'stdef'+cfmt.fullsvg) +return} +for(i=0;i'} +y=staff_tb[st].y +if(!cache&&w>get_lwidth()-10&&staff_tb[st].staffscale==1){cache={st_l:stafflines,st_w:w|0} +i='stdef'+cfmt.fullsvg;if(ln.indexOf('\n'+ln+'\n';xygl(x1,y,i) +return} +out_XYAB('\n'+ln+'\n\n',x1,y)} +function draw_bar(s,bot,h){var i,s2,yb,w,bar_type=s.bar_type,st=s.st,p_staff=staff_tb[st],x=s.x +if(st!=0&&s.ts_prev&&s.ts_prev.type!=C.BAR) +h=p_staff.topbar*p_staff.staffscale;s.ymx=s.ymn+h;set_sscale(-1) +anno_start(s) +if(s.color) +set_color(s.color);yb=p_staff.y+12;if(p_staff.stafflines!='|||||') +yb+=(p_staff.topbar+p_staff.botbar)/2-12 +if(s.bar_mrep){set_sscale(st) +if(s.bar_mrep==1){for(s2=s.prev;s2.type!=C.REST;s2=s2.prev);xygl(s2.x,yb,"mrep")}else{xygl(x,yb,"mrep2") +if(s.v==cur_sy.top_voice) +nrep_out(x,yb+p_staff.topbar,s.bar_mrep)} +set_sscale(-1)} +if(bar_type=='||:') +bar_type='[|:' +for(i=bar_type.length;--i>=0;){switch(bar_type[i]){case"|":if(s.bar_dotted){w=(5*p_staff.staffscale).toFixed(1);out_XYAB('\n',x,bot,w,h)}else if(s.color){out_XYAB('\n',x,bot,h)}else{sb+='M'+sx(x).toFixed(1) ++' '+self.sy(bot).toFixed(1) ++'v-'+h.toFixed(1)} +break +default:x-=3;if(s.color) +out_XYAB('\n',x+1.5,bot,h) +else +thb+='M'+sx(x+1.5).toFixed(1) ++' '+self.sy(bot).toFixed(1) ++'v-'+h.toFixed(1) +break +case":":x-=2;set_sscale(st);xygl(x+1,yb-12,"rdots") +set_sscale(-1) +break} +x-=3} +set_color();anno_stop(s)} +function out_bars(){var i,b,bx,l=ba.length +set_font("annotation");bx=gene.curfont.box +if(bx) +gene.curfont.box=0 +for(i=0;i\n' +if(thb) +output+='\n'} +function hl_rest(s){var j,p_st=staff_tb[s.st],i=5-s.nflags,x=s.x,y=s.y +if(i<6) +return +if(i==7&&y==12&&p_st.stafflines.length<=2) +y-=6 +j=y/6 +switch(i){default:switch(p_st.stafflines[j+1]){case'|':case'[':break +default:set_hl(p_st,j+1,x,-7,7) +break} +if(i==9){y-=6 +j--} +break +case 7:y+=6 +j++ +case 6:break} +switch(p_st.stafflines[j]){case'|':case'[':break +default:set_hl(p_st,j,x,-7,7) +break}} +function st1(st,s){var tim=s.time +do{s=s.ts_next}while(s.st!=st) +while(s.prev&&s.prev.time>=tim) +s=s.prev +if(s.bar_type) +return s.x +return s.x-s.wl} +for(st=0;st<=nstaff;st++){stl[st]=cur_sy.st_print[st] +xstaff[st]=!stl[st]?-1:0} +bar_set();draw_lstaff(0) +for(s=tsfirst;s;s=s.ts_next){switch(s.type){case C.STAVES:sy=s.sy +for(st=0;st<=nstaff;st++){x=xstaff[st] +if(x<0){if(sy.st_print[st]){xstaff[st]=st1(st,s) +stl[st]=true} +continue} +if(sy.st_print[st]&&cur_sy.staves[st]&&sy.staves[st].stafflines==cur_sy.staves[st].stafflines) +continue +if(s.ts_prev.bar_type){x2=s.ts_prev.x}else{x2=(s.ts_prev.x+s.x)/2 +xstaff[st]=-1} +draw_staff(st,x,x2) +xstaff[st]=sy.st_print[st]?x2:-1} +cur_sy=sy;bar_set() +continue +case C.BAR:if(s.invis||!s.bar_type||!cur_sy.st_print[s.st]) +break +if(s.second&&(!s.ts_prev||(s.ts_prev.type==C.BAR&&s.ts_prev.st==s.st))) +break +ba.push([s,bar_bot[s.st],bar_height[s.st]]) +break +case C.STBRK:if(cur_sy.voices[s.v]&&cur_sy.voices[s.v].range==0){if(s.xmx>14&&s.next&&s.next.type==C.CLEF){var nv=0 +for(var i=0;i0) +nv++} +for(s2=s.ts_next;s2;s2=s2.ts_next){if(s2.type!=C.STBRK) +break +nv--} +if(nv==0) +draw_lstaff(s.x)}} +st=s.st;x=xstaff[st] +if(x>=0){s2=s.prev +if(!s2) +break +x2=s2.type==C.BAR?s2.x:s.x-s.xmx +if(x>=x2) +break +draw_staff(st,x,x2) +xstaff[st]=s.x} +break +case C.GRACE:for(s2=s.extra;s2;s2=s2.next) +self.draw_hl(s2) +break +case C.NOTE:if(!s.invis) +self.draw_hl(s) +break +case C.REST:if(s.fmr||(s.rep_nb&&s.rep_nb>=0)) +center_rest(s) +if(!s.invis) +hl_rest(s) +break}} +for(st=0;st<=nstaff;st++){x=xstaff[st] +if(x<0||x>=realwidth) +continue +draw_staff(st,x,realwidth)} +draw_all_hl() +out_bars() +draw_vname(indent,stl)} +Abc.prototype.draw_symbols=function(p_voice){var bm={},s,x,y,st;for(s=p_voice.sym;s;s=s.next){if(s.invis){switch(s.type){case C.CLEF:if(s.time>=staff_tb[s.st].clef.time) +staff_tb[s.st].clef=s +continue +case C.KEY:p_voice.ckey=s +default:continue +case C.NOTE:break}} +st=s.st +x=s.x;set_color(s.color) +switch(s.type){case C.NOTE:set_scale(s) +if(s.beam_st&&!s.beam_end){if(self.calculate_beam(bm,s)) +draw_beams(bm)} +if(!s.invis){anno_start(s);draw_note(s,!bm.s2);anno_a.push(s)} +if(s==bm.s2) +bm.s2=null +break +case C.REST:if(!gene.st_print[st]) +break +draw_rest(s);break +case C.BAR:break +case C.CLEF:if(s.time>=staff_tb[st].clef.time) +staff_tb[st].clef=s +if(s.second||!staff_tb[st].topbar||!gene.st_print[st]) +break +set_color();set_sscale(st);anno_start(s);y=staff_tb[st].y +if(s.clef_name) +xygl(x,y+s.y,s.clef_name) +else if(!s.clef_small) +xygl(x,y+s.y,s.clef_type+"clef") +else +xygl(x,y+s.y,"s"+s.clef_type+"clef") +if(s.clef_octave){if(s.clef_octave>0){y+=s.ymx-10 +if(s.clef_small) +y-=1}else{y+=s.ymn+6 +if(s.clef_small) +y+=1} +xygl(x-2,y,(s.clef_octave==7||s.clef_octave==-7)?"oct":"oct2")} +anno_a.push(s) +break +case C.METER:p_voice.meter=s +if(s.second||!staff_tb[s.st].topbar) +break +set_color();set_sscale(s.st);anno_start(s);draw_meter(s);anno_a.push(s) +break +case C.KEY:p_voice.ckey=s +if(s.second||!staff_tb[s.st].topbar) +break +set_color();set_sscale(s.st);anno_start(s);self.draw_keysig(x,s);anno_a.push(s) +break +case C.MREST:draw_mrest(s) +break +case C.GRACE:set_scale(s);draw_gracenotes(s) +break +case C.SPACE:case C.STBRK:break +case C.CUSTOS:set_scale(s);draw_note(s,0) +break +case C.BLOCK:case C.REMARK:case C.STAVES:case C.TEMPO:break +default:error(2,s,"draw_symbols - Cannot draw symbol "+s.type) +break}} +set_scale(p_voice.sym)} +function draw_all_sym(){var p_voice,v,n=voice_tb.length +function draw_sl2(){var i,a,d,dy,dy2,dy2o,dz,n,sl +while(1){sl=gene.a_sl.shift() +if(!sl) +break +i=sl[3].indexOf('d="M')+4 +output+=sl[3].slice(0,i) +a=new Float32Array(sl[3].slice(i).match(/[\d.-]+/g)) +a[1]-=staff_tb[sl[0].st].y +dy2o=sl[0].fmt.sysstaffsep+24 +dy2=staff_tb[sl[1].st].y-staff_tb[sl[0].st].y +switch(sl[2]){case"//":case"\\\\":d=-(sl[1].prev.prev.y+staff_tb[sl[0].st].y ++sl[1].prev.next.y+staff_tb[sl[1].st].y) +-2*(a[1]-posy) +a[5]=d-a[5] +a[7]=d-a[7] +if(a.length>8){d=sl[2][0]=='/'?3:-3 +a[8]=-a[8] +a[10]=-a[3]+d +a[12]=-a[5]+d +a[14]=-a[7]} +break +case"/\\":case"\\/":d=sl[2][0]=='/'?dy2-dy2o-10:dy2+dy2o+10 +a[3]+=d +a[5]+=d +if(a.length>8){a[10]+=d +a[12]+=d} +break +default:d=sl[2][0]=='/'?dy2-dy2o:-dy2-dy2o +a[5]+=d +a[7]+=d +if(a.length>8){a[12]-=d +a[14]-=d} +break} +output+=a[0].toFixed(1)+' '+a[1].toFixed(1) ++'c'+a[2].toFixed(1)+' '+a[3].toFixed(1) ++' '+a[4].toFixed(1)+' '+a[5].toFixed(1) ++' '+a[6].toFixed(1)+' '+a[7].toFixed(1) +if(a.length>8) +output+='v'+a[8].toFixed(1) ++'c'+a[9].toFixed(1) ++' '+a[10].toFixed(1) ++' '+a[11].toFixed(1) ++' '+a[12].toFixed(1) ++' '+a[13].toFixed(1) ++' '+a[14].toFixed(1) +output+='"/>\n'}} +for(v=0;v=22?C.SL_ABOVE:C.SL_BELOW +else if(s.multi) +dir=s.multi>0?C.SL_ABOVE:C.SL_BELOW +else +dir=s.stem<0?C.SL_ABOVE:C.SL_BELOW +if(s.multi){for(i=0;i<=s.nhd;i++){ty=s.notes[i].tie_ty +if(!((ty&0x07)==C.SL_AUTO)) +continue +s.notes[i].tie_ty=(ty&C.SL_DOTTED)|dir} +continue} +if(ntie<=1){for(i=0;i<=s.nhd;i++){ty=s.notes[i].tie_ty +if(ty){if((ty&0x07)==C.SL_AUTO) +s.notes[i].tie_ty=(ty&C.SL_DOTTED)|dir +break}} +continue} +if(!sec){if(ntie&1){ntie=(ntie-1)/2;dir=C.SL_BELOW +for(i=0;i<=s.nhd;i++){ty=s.notes[i].tie_ty +if(!ty) +continue +if(ntie==0){if(s.notes[i].pit>=22) +dir=C.SL_ABOVE} +if((ty&0x07)==C.SL_AUTO) +s.notes[i].tie_ty=(ty&C.SL_DOTTED)|dir +if(ntie--==0) +dir=C.SL_ABOVE} +continue} +ntie/=2;dir=C.SL_BELOW +for(i=0;i<=s.nhd;i++){ty=s.notes[i].tie_ty +if(!ty) +continue +if((ty&0x07)==C.SL_AUTO) +s.notes[i].tie_ty=(ty&C.SL_DOTTED)|dir +if(--ntie==0) +dir=C.SL_ABOVE} +continue} +pit=128 +for(i=0;i<=s.nhd;i++){if(s.notes[i].tie_ty){if(pit<128&&s.notes[i].pit<=pit+1){ntie=i +break} +pit=s.notes[i].pit}} +dir=C.SL_BELOW +for(i=0;i<=s.nhd;i++){ty=s.notes[i].tie_ty +if(!ty) +continue +if(ntie==i) +dir=C.SL_ABOVE +if((ty&0x07)==C.SL_AUTO) +s.notes[i].tie_ty=(ty&C.SL_DOTTED)|dir}}} +function set_tie_room(){var p_voice,s,s2,v,dx,y,dy +for(v=0;v24&&s.notes[s.nhd].tie_ty&&(s.notes[s.nhd].tie_ty&0x07)==C.SL_ABOVE);else +continue +s2=s.next +while(s2&&s2.type!=C.NOTE) +s2=s2.next +if(s2){if(s2.st!=s.st) +continue +dx=s2.x-s.x-10}else{dx=realwidth-s.x-10} +if(dx<100) +dy=9 +else if(dx<300) +dy=12 +else +dy=16 +if(s.notes[s.nhd].pit>24){y=3*(s.notes[s.nhd].pit-18)+dy +if(s.ymxy) +s.ymn=y +if(s2&&s2.ymn>y) +s2.ymn=y;y_set(s.st,false,s.x+5,dx,y)}}}} +var musicfont='url("data:application/octet-stream;base64,\ +AAEAAAAOAIAAAwBgRkZUTZHVOuoAAFgMAAAAHEdERUYAFQAUAABX8AAAABxPUy8yWMFdCgAAAWgA\ +AABWY21hcHlUuFMAAAQIAAAD5mN2dCAAIgKIAAAH8AAAAARnYXNw//8AAwAAV+gAAAAIZ2x5ZqUq\ +QgUAAAkcAABF/GhlYWQVmDJzAAAA7AAAADZoaGVhCWn/GwAAASQAAAAkaG10eNm9+0EAAAHAAAAC\ +SGxvY2HHqraAAAAH9AAAASZtYXhwANkBEgAAAUgAAAAgbmFtZeq7sB0AAE8YAAADFXBvc3SIfxKM\ +AABSMAAABbUAAQAAAAEAANGbj/xfDzz1AAsEAAAAAADRlyIXAAAAAOBezej/OPzvBUsEiAAAAAgA\ +AgAAAAAAAAABAAAEiPzvAFwEJf84/XQFSwABAAAAAAAAAAAAAAAAAAAAkgABAAAAkgDhAAUAAAAA\ +AAIAAAABAAEAAABAAC4AAAAAAAEBgAGQAAUACAKZAswAAACPApkCzAAAAesAMwEJAAACAAUDAAAA\ +AAAAAAAAARAAAAAAAAAAAAAAAFBmRWQAQAAA7LcDOP84AFwEiAMRAAAAAQAAAAAAAAF2ACIAAAAA\ +AVUAAAGQAAACWAAAAFcAAAAjAAAAJQAAACT//wBkAAAEIwAABCUAAAHg/9wDugAAAwsAAALSAAAC\ +v/+6AdYAAAMLAAADDgAAAyf/yADIAAABaAAAAa4AAAEiAAABkAAAAXwAAAGQAAABkAAAAYEAAAGQ\ +AAABkAAAAYEAAAGZAAkBmAAJAfQAAAEEABQBBAAKAmsAJAISAAABwgAAAUIAAAFAAAABSv/+ASwA\ +AAIwAAABSgAAAUoAAABkAAABQAAAAUAAAAFAAAABQAAAAGQAAAE2AAAA5gAAATYAAAE7AAABOwAA\ +ATsAAAE7AAABOwAAATsAAAE7AAABOwAAATsAAAE7AAABDQAAAMgAAAD/AAABCwAUAW4AAACMAAAA\ +jAAAAQ0AMgFu//UAqQAAAToAAAFA//0AUAAAAVQAAABkAAABGAAAAlgAAAC2AAABkAAFAIIAAACC\ +AAABLAAAASwAAADuAAAA/wAAAUkAAAGPAAAB2AAAAdgAAAIz//ADIP/hAXv/tAG4/9sBFv9+ARP/\ +2wDcAAAA6P/kAr//tAIz/7QCv/+0Ayv/2wFf/9sCaf9+AV//fgJp/34BXwAAAf0ABQG1AAABtQAA\ +AkQADQJEAA0BGAAAATYAAAEs//8BLAAAAPoAAADIAAABGP84APoAAADIAAAEDQAAAhwADAH0AAAB\ +9AAAAfQAAAH0AAAB9AAAAfQAAAB4AAAALQAAAhwAAAD6AAAA+v/oAcIAAAFIAAABQAAAAgoAAAIK\ +AAAAZAAAAAAAAwAAAAMAAAAcAAEAAAAAAuAAAwABAAAAHAAEAsQAAABgAEAABQAgAAAAIOAA4DDg\ +OeBI4FDgXOBi4GngjOCV4KTgqeCz4QHhu+Hn4gDiSeJk4mvig+Ss5MDk0eTq5QHlMeU55W3lguXQ\ +5eLmGOYk5jDmUOZV6RjpIOkl6V3qAuqk7Knst///AAAAAAAg4ADgMOA44EPgUOBc4GLgaeB64JTg\ +oOCp4LPhAeG54efh8uJA4mDiauKA5KDkwOTO5OHlAOUg5TnlZuWC5dDl4uYQ5iTmMOZQ5lXpEOkg\ +6SXpXeoC6qTsouy3//8AA//kIAUf1gAAAAAfvh+zH64fqAAAAAAAAB+CH3kfLAAAHkkAAAAAAAAA\ +AAAAAAAbkwAAAAAAAAAAGzcAABr0GqcalgAAGlkaThovGisAABdnF2MXLBaIFecAABPaAAEAAAAA\ +AAAAAABYAFoAAAAAAAAAAABcAIAAggAAAAAAAACEAAAAhgCiALQAvAC+AMQAAADaAOAA8gD0AAAB\ +FAAAAAAAAAEcAAAAAAAAAAABJAAAAAAAAAAAAAABKgAAAAAABwAIAAkAAAAKAAsADAANABIAEwAU\ +ABUAFgAAABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgAuAAAALwAxAAAA\ +MgAAAAAAMwAAADQAAAAAADUAAAA2ADcAOAA5ADoAOwA8AD0APgA/AEAAQQBCAEMARABFAEYARwBI\ +AEkASgBLAEwATQBOAAAATwAAAFAAAAAAAAAAUQAAAAAAAABSAFQAAAAAAFUAVgBXAFgAWQBaAFsA\ +XABdAF4AXwBgAGEAYgBjAGQAZQBmAGcAAAAAAAAAaABpAGoAawBsAAAAbQBuAG8AcQByAAAAcwAA\ +AAAAdAB1AHkAAAB6AAAAewAAAAAAAAB8AIEAggCDAAAAhACFAAAAAACGAIwAjQAAAI4AAACPAAAA\ +kAAAAQYAAAMAAAAAAAAAAQIAAAACAAAAAAAAAAAAAAAAAAAAAQAABAAAAAAAAAAAAAAAAAAAAAAA\ +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiAogAAAAqACoAKgA2AD4AbgB6\ +AIYAkgCwASQBgAHwAjQCwANSA7IDxgRkBPYFTgWOBhAGMgZGBpgG6AcIB04HjgfICBQIVAiaCPQJ\ +CAkuCVQJhgmiCcQJ7AoCChwKUgpgCmwKeAqKCqYKyArWCuoK/AsMC1gLaAuCC5wL1AwMDFoMpg0M\ +DW4N7A5kDooOpA7UDv4PVg9wD4oPsBAKECoQaBCQEKQQsBC+EM4Q9hEeEVYRYhFuEXoRhhGoEc4S\ +BBJSErITJhNME3oTzhRCFJYU1hUaFXAWihdiF/gYqhlsGf4a0BvkHNAdEh1WHaAduh3eHfIeBh42\ +HkYeWB50Hooerh7eH7ogdiCeILwg7iEsIV4hoCGyIcAh3CH2IhwiPiJsIoYisCLsIv4AAAACACIA\ +AAEyAqoAAwAHAC6xAQAvPLIHBADtMrEGBdw8sgMCAO0yALEDAC88sgUEAO0ysgcGAfw8sgECAO0y\ +MxEhESczESMiARDuzMwCqv1WIgJmAAABAAAAAAGRAZAAAwAAMREhEQGRAZD+cAABAAAAAAAAAAAA\ +AAAAMQAAAQAAAAAAVwQDACAAABE1NjU0JyY1NDcGFRQXFhUUBxYVFAcGFRQXJjU0NzY1NDUjElc/\ +FSVNTSUVP1cSIwIDAhhDNmA5NGY6MksiOWFNYRgYZkxgOSVKMjpmNDlgNkgAAQAAAAAAIwPoAAMA\ +ABEzESMjIwPo/BgAAQAAAfQAJQPoAAMAABEzESMlJQPo/gwAAf//A2QAJARgAAMAABEzByMkASQE\ +YPwAAgAAAUAAZAKeAAcADwAAEiImNDYyFhQCIiY0NjIWFEcqHR0qHR0qHR0qHQI6HSodHSr+6R0q\ +HR0qAAAABQAAAAAEJAGuAC8ANwA/AEcAUwAAITUzHgEzMjY1NCcuBDU0NjMyFhc3MxcjLgEjIgYV\ +FB4DFx4BFRQGIyInByAiJjQ2MhYUBCImNDYyFhQBETMyNjQmIwM1MxEjNTMyFhUUIwJOHhVPMik7\ +lBkaKhYRWT0kJxkeHgceD0owHzkQIhkyCE5NW09FLiMBmyodHSod/eoqHR0qHf6JKDxGRjzcRkbc\ +cYn6oDxLICEtKAcIFBQjFUNNCw4ZmzpIKBsPFw8JCwIVNzM6TiAgHSodHSodHSodHSoBaf6YYaZh\ +/noeAWgeZ2vSAAUAAAAABCQBrgAaACIAKgAyAD4AACEiJjU0NjMyFhc3MxcHJiMiBhQWMzI2NxcO\ +ATIiJjQ2MhYUBCImNDYyFhQBETMyNjQmIwM1MxEjNTMyFhUUIwMCZ3WCWiUpGx4eCCEkXjg2Njg2\ +TREjFFmxKh0dKh396iodHSod/okoPEZGPNxGRtxxifprZ2V3DBIepgSMbZhtST4KSlEdKh0dKh0d\ +Kh0dKgFp/phhpmH+eh4BaB5na9IAAAAD/9wAAgHeArMABwAPAE0AAAAiJjQ2MhYUBCImNDYyFhQX\ +NDYzMhYVFAcWMzI2NTQvAQMnEy4BNTQ+ATc2MzIWFRQGIyImNTQ3JiMiBhUUHwETFwMeARUUDgEH\ +BiMiJgGeIBgYIBj+fiAYGCAYWxsUEx4sFykmNiZ7zyvRWkgcExQmMzA4GxQTHiwXKSY2JnrUK9Va\ +SBwTFCYzMDgBUhggGBggVBggGBgg0xIcGhEdDhctJi0mZf7eIAElR3E4Fi0RERM4IRIcGhEdDhct\ +Ji0mZQEmH/7XR3I4Fi0RERM4AAUAAP9WA7wDYgAXABsAHwAjACcAAAEzFR4BFzMVIw4BBxUjNS4B\ +JyM1Mz4BNxEjFhcTETY3JzMmJwMRBgcBxDRwnwmsrAmfcDRwnwmsrAmecZQHjTSOBpSUBo40jgYD\ +YqsLtoA0f7cLq6sLt380grQL/ov0FAEI/vgU9DT0FP74AQoU9gAAAAAEAAD9bwKnBIgACwBEAE4A\ +YwAAAQYVFBc+ATU0Jw4BExcVFAYjIiY1NDYzMhYUBgcWMzI2PQEnBiMiJjU0Nz4GPwEmNTQ2NxYV\ +FAYHFzYzMhYVFCc0JiMiBiMTPgEnDgEVFBYXLgE1NDcnDgEHHgEzMjcBbAcFSHU2OUJLF09NUl9A\ +Mi9BPy8tGC9AFxwNmNs6DSQgLh0xEhcWDWxJYlx5FBQLcYhOYlACBwIhW0HiNkYnHT0/phGPbAEC\ +oXkKFgNTNy0jWjGVSHINB177bOwDTFxTQS1IO1g3ARk8RgPpAsedhWYXMCUtGicOERCWZoyhBzrl\ +iKlfzwKcc866PmYB/p0SXfILRjEgQhIORUepLr1yl2eNowIAAgAA/fwC0gIAAGQAaAAAATI2NTQn\ +JiMiBw4CByYnJicRIxEzETY3NjceAxcWMzI2NTQnJiMiBxYXFBYVFAYrASY1NDc2NzYzMhcWFxUU\ +BgcGIyInBxc2MzIWFxYdAQYHBiMiJjU0NzMyFhUUBhUGBxYBMxEjAd4+Sg0aSUY8AgYKBCIaHi4c\ +HC4eGiIGFAwZDyclMT0SJFIvMTINAjMhBUQFGlUnI15VNwhaSB8tND8iIj80QmYeKAg3Vl1MckQF\ +ITMCDzA1/k17e/4geEotLWxJBQ4bCmInKx/+BAQA/hEfKydiCyoXHQkbe0IxNmIaECgDDQQeKxky\ +FAtEGQ1PNFMSTm8cDRdLShc+LDlDElM0UE48MhkrHgQOBCYQHAPe/AAAAAP/uv2lAsMA/wAqADYA\ +QgAANzQ2MzIWFxYVFAYHDgEHNjc+ATc2NTQmJy4BIyIGBz4BMzIWFRQHBiMiJgUiJjU0NjMyFhUU\ +BiciJjU0NjMyFhUUBhOLZ1VrKy9GVWbPleWFMTQTChEdHDQzO2EWGCMbLj0nITEzRQKCFh0aFBUe\ +GhoXGxwVFBwbHWKANDk+cn+6T2BNCD2FMWNQK2ZHUCMiF09GHhdBLzIgHlGKHBcWHB0VFh3yHhkV\ +GhsUGR4AAAIAAP8GAXIA+gADAAcAADczEyMDMxMj3JQCltyUApb6/gwB9P4MAAAEAAD+CgIfA6oA\ +CQAgAGMAbgAAJRYXPgE1NCYjIgMCJw4BFRQXLgE1NDY3JicOAQceATMyFx4BHwEwHQEUIyImNTQ2\ +MzIWFRQGBxYzMjY1NC8BBiMiJjU0Nz4BNz4CNyY1NDY3HgEVFAYHHgEXNjMyFxYVFAcGAwYVFBc+\ +ATU0JwYBSRMGTUdWQg4NGAEsOR8gKkw7BQlxVQEFbYIDIgMGAgJ6NlMzKCU1MScVIiMsAQ0JFY2Z\ +Lg5MHgUjJxIOWUAvGUlhAggEEghcOTJjNmQDBjdeKV91xFsSTzM2Vv7gAQsQCTQnKSYSQSk4ThA+\ +Wlp5U3F+GiBAFxcbB39IMSQzNyQiKAEMMzUPCY0BkopqURxQGAQgIQ3CB25/EzNiW22HTBFuJAJD\ +NmJ3MBsDVh4iOSQlezNGJiYAAAAAAgAA/mMCQgGaAGMAZwAAATI2NTQnJiMiBw4BByYnJicRIxEz\ +ETY3NjceAxcWMzI2NTQnJiMiBxYXFBYVFAYrASY1NDc2NzYzMhcWFxUUBgcGIyInBxc2MzIWFxYd\ +AQYHBiMiJjU0NzMyFhUUBhUGBxYBMxEjAX4yOwoVOjgwAgwEHRMYJRYWJRgTHQQRChMMIB0nMQ4c\ +QyUnKAoCKhoENgQVRCMYSUYtBkg6GCUrMRsbNCg1UhggBi1GST1bNgQaKgIMJiv+o2Ji/oBgOyQk\ +VzsFHQtSHCIZ/moDM/50GSIcUggiEhcIFmI1KyhOFQ0gAgsDGCIUKBAJNRULQCtBDj5aFgoSPDsT\ +MiMuNQ9BK0A/MCgUIxgDCwMfDRYDGPzNAAAAAAP/yP4eAjYAzAAmAC8AOwAANzQ2MzIXFhUUBw4B\ +BzY3Njc2NTQnLgEjIgYHPgEzMhYVFAcGIyImBSImNDYyFhQGJyImNTQ2MzIWFRQGD3BSgjwjeEPG\ +acBeRx4JLRgtIDBSChIWFiU5IBklLDkCAhIXFCIYFRUSFhUSERYVIU1eVzNaxmw7VwYzZEqMLylm\ +MxoUQjYXDTwmKBoYU3UXJBYXIhjCGBQRFBUQExkAAAAAAwAAAAAAyADwAAkAFQArAAA3BhUUFjMy\ +NjU0JyIGFRQeARc2NTQmByImNTQ2Ny4BNTQ2MzIWFRQHFhUUBlYsHhARFAIOEwoJECUUNCU1IyIR\ +DCgeKTE3IzR0EB4UHhsRF4URDgoQCAwLHQ4X3CgeFxoLDxMQGiIfHSUOHB8gJgACAAD//wFnAQMA\ +MABiAAAXIjU0MzIVFAYVFDMyNjU0IyIHBiMiNTQ3NjM2MzIWFRQHBgcOAQcGFjc2MzIWFRQGJyI0\ +OwEyNzI0MzY3PgE1NiMiBwYnJjc+ATc2OwIyFxYzMjc2BwYHBhUUMzIVFCsC1D4YGxIdFyweChQW\ +CAsmBgkjSQ8FFhpICgYBAgUIDA4gI0P6CQkJFAUBAQ0XAQIIDgkIDA0KDwYRAjIEAgMGDgwIAggX\ +BkcKAhUMDCcnATchGwcWBg5CIxwMDQ8WVAsBAgUQBwkQAgYLCgUCAyQgKDkBEgwEHz0BBQIVCQ4G\ +BhIHGAJABwYCCBKtHQUFCAoIAAAAAAIAAP8GAa4A+gALABQAADMUFjMyNjU0JiMiBgc0NjIWFAYi\ +JooqIyIrJyYlKIp9tH19tH1ieHlhZXV2YWeQkdKRkgAAAQAA/wYBIgD6AAkAADE3MxEXFSM1NxFk\ +fUHwQfr+Ph4UFB4BLAAAAAEAAP8GAY8A+gA8AAA3MhUUBw4DBzYzMhYzMjc+AjMOAgcGBwYjIiYj\ +IgYjIjU0Jz4FNTQnIgcyFhUUBiMiNTQ+AcfIBQ02QG82EyAbZBwYHgUQDAEBBQUBBxAaKRp0FR9W\ +AgcBAiw+RzwoU04aHCk3Hkw8WPp+Gg4hLh1ELQwjDgMNCwUWFgMpDhgnJhABAiFFODwyOBhiATUl\ +Hh8pZyg5GQAAAQAA/wYBdQD6ADkAADcyFhUUBiMiJjU0NzYzMhcWFRQGBx4BFRQHBiMiJicmNDYz\ +MhYVFAYjFjMyNjU0JicmNDc+ATQmIyJmGyInIRsyHzNZRiZERj0+UUskTSdXGCMyICIqJRsMPyQr\ +SC4WFi9LKSQ8qhwXGyMrIy8aKhMiSC5ECwtFLUMnExYUHUwuIRsZHikxJyY6CAQiBAk3UDAAAAEA\ +AP8GAZAA+gARAAAFFyM3NSM1NjUzATM/AREzFSMBRTLIMuGTo/77sAFjS0vRKSkxKPKA/o6Wkf7Z\ +KAAAAAABAAD/BwF+APoALwAAFzYzMhYVFAYjFjMyNz4BNTQnJiMiBxMhDgErAQc2MzIXHgEVFAcO\ +AiMiJy4BNTQSHCEbKiAcGiQxHBMJHhwoTkgKAWILNSXVBjlCUzEhK0MWQCslPCsQHl8gIBccICEe\ +FB8gORwaNQEiJDp5Hh8VQSVPLxAQAhQJMhIjAAAAAAIAAP8GAYEA+gAJACwAABcyNjU0JiMiBxYT\ +FhUUBiMiJjU0NjMmIyIGFT4CMzIWFRQGIyImJz4BMzIWyCktKigsMAfdGyMYHiIbEBY3NS8VGC0e\ +TE9xSGFmAQFsWzA/0kUsIjAlngGfGiYZKB4bDB4jeF8LCglANkRZgnhpkRIAAQAA/wYBkAD7ACgA\ +ADciDgMHNz4IMzIWMzI2Nw4EFSM2NzY3NjcGIyImYRQaFAkRBQoBDAILBQsJDRAJL3UjGjsRG0Ua\ +HgiCAQgRaB4tER4lYKEFDgkbBnQBCwIJAQYBAwEmFw5Dp0FcQitFGzaHJzkKKAADAAD/BgGEAPoA\ +DgAcADQAABcOARUUFjMyNjU0LgM3PgE1NCYiBhUUHgMHLgE1NDY3MhYVFAYHHgEVFAYjIiY1NDaa\ +NixYLCo/DyAdMT4zI0RSMwocEjFwMSlmSktlKjA6MnVNTHY5KhkkGx0wKR8OFxINE1oaIhwdMCgg\ +DxcTChU/GDw1M00BRjInNBcaOjU3SkgwJDUAAAIAAP8GAYEA+gAJACwAADciBhUUFjMyNyYDJjU0\ +NjMyFhUUBiMWMzI2NQ4CIyImNTQ2MzIWFw4BIyImuSktKigsMAfdGyMYHiIbEBY3NS8VGC0eTE9x\ +SGFmAQFsWzA/0kUsIjAlnv5hGiYZKB4bDB4jeF8LCglANkRZgnhpkRIAAQAJ/woBmQD5ADAAACUw\ +FzY1NCYjDgEVFBcWMzI3NjcUHgEVDgEHIicmJzQmNTQ3MhYXFhUUBiMiJjU+ATMBLxIEPB8zQCch\ +MCsoHCoJCBtVVk87OwQB2yRAESIkHCApAiAapAMFCBQeAmJqjjMqIhhYAQQDAVVQATk5ZgIrAuYC\ +HhQnJCU5LhwWJgACAAn+ogGZAV4AOAA/AAAlMBc2NTQmIyIHERYzMjc2NxQeARUOAQcjFSM1Jicm\ +JzQmNTQ3NTMVMjYzMhYXFhUUBiMiJjU+ATMDEQYVFBcWAS8SBDwfAxAMDCsoHCoJCBtRVQEjPS47\ +BAGrIwMHAyRAESIkHCApAiAaej0nCqQDBQgUIgT+SwQiGFgBBAMBVFEBaGsJLTlmAisCyxlpZgEe\ +FCckJTkuHBYm/pcBli6RjjMNAAABAAD/BgH0APoACwAANTM1MxUzFSMVIzUj10bX10bXI9fXRtfX\ +AAAAAQAU/gYA5AIAABMAABMWBwYnJgI1NBI3NhcWBwYCFRQS3AcNCQVJa2tJCQsHBzxGR/4WCAUD\ +BlcBIH18ASJWCwcFCUn+54iG/uUAAAEACv4CANwB/QATAAATNhI1NAInJjc2FxYSFRQCBwYnJhQ7\ +R0Y8CQwKB0lra0kICwb+FkkBG4aIARlJCwQECVb+3nx9/uBXCQkEAAAEACT/VgJMAKoACwAPABMA\ +HgAABTQmIyIGFRQWMzI2NzMRIwEzESMkFAYjIiY1NDYzMgGoVDwgNFc9IS9yMjL+CjIyAfV7ZmV8\ +eWhmIDVOJx81SyPq/qwBVP6s5nhGSTk/QwAAAgAA/yQCEgDcAAMADwAANxUhNSUzFSE1MxEjNSEV\ +Ix4B1v4MHgHWHh7+Kh5BgoKbNzf+SDc3AAACAAD/fwHCAIEACwATAAAFNCYjIgYVFBYzMj4BFAYi\ +JjQ2MgFRWjYgNF03IS9xfch9fcgeNE8nHzRMI3RsS0tsSwAAAAIAAP92AUIAigAMABgAACU0JiMi\ +BhUUHgEzMjYnMhYVFAYjIiY1NDYBIxocO5MEGxc7k2FAQHtHQEB7Lg4ZWygEDhVbhEwpP2BMKT9g\ +AAAAAQAA/3kBQACHAAsAACUUBiMiJjU0NjMyFgFAeVkyPHpYMjwoRmk4J0VqOAAB//7/bwFMAJEA\ +CwAAJzcXNxcHFwcnByc3AhuMjBuGhhyLixyGcSB2dSBwcCF1dSFwAAAABQAA/2oBLACWAAUACwAR\ +ABcAHwAAFwcWMzI3LwEGFRQXPwEmIyIHHwE2NTQnBjQ2MhYUBiKWRx0qKR9dRx0dXEgfKSodXEcd\ +HfJYfFhYfBJIHR1aSR8pKh1YSB0dWkgfKSodhXxYWHxYAAAAAQAA/wYCMAD6AAMAABUBMwEBuHj+\ +R/oB9P4MAAEAAP90AUoAjAADAAAxNxcHpaWljIyMAAEAAP90AUoAjAACAAAVGwGlpYwBGP7oAAEA\ +AP/OAGQAMgAHAAAWIiY0NjIWFEcqHR0qHTIdKh0dKgAAAAEAAP95AUACqAAPAAARIREUBiMiJjU0\ +NjMyFxEhAUB5WTI8elgxH/7eAqj9gEZpOCdFahwBxQAAAQAA/3kBQAKoABMAABEhERQGIyImNTQ2\ +MzIXESE1ITUhAUB5WTI8elgxH/7eASL+3gKo/YBGaTgnRWocARF4PAAAAAABAAACMAFAAqgAAwAA\ +ESEVIQFA/sACqHgAAAAAAgAAAXwBQAKoAAMABwAAESEVIREhFSEBQP7AAUD+wAH0eAEseAAAAAEA\ +AP/aAGQAPgAHAAAWIiY0NjIWFEcqHR0qHSYdKh0dKgAAAAEAAAMCATYDwAAFAAARIRUhFSMBNv7o\ +HgPAHqAAAAAAAQAAAyoA5gQ4ADkAABMiJjU0NjMyFhUUDwEUMzI2NTQmKwEiNTQ7ATI2NTQmIyIV\ +FBYVFCMiJjU0NjMyFhUUDgEVFBYVFAZQJykQDg8SCwwhFSQRDxIUFCgXJQ4OFQIhDBIlKyMtIyMU\ +MwMqHhgOFhAMEgcHDSofDxkOECUVDRMNAwgIHBEMFR4iGhgjEwIBIg8jLQAAAAABAAADAgE2A8AA\ +BQAAARUjNSE1ATYe/ugDwL6gHgAAAAEAAPzvATsAAAAPAAAVNTMeBBUUBzY1NCYnHgY/UVA3LhKR\ +cO/vNXBlbIlJYGlBSY/fKgABAAAAAAE7AxEADwAAMTUzPgE1NCcWFRQOAwcecJITLjdQUT8G7x/h\ +k0dIZ15Iim1ncTUAAgAA/UQBPAAAABgAJgAAGQEzHgYVFAcWFRQHNjU0LgMjNR4DFzQ2NTQuAx4G\ +JzM7OC4cEhMeBSk+SEAWCURNVhUBKT5IQf6pAVcbOTM3Oj1IJSwrKSs2ORkhN2RFNBirJFRDXSkE\ +DAQ3ZUUzGQAAAAIAAP//ATwCvAAYACYAABURMzI+AzU0JxYVFAcWFRQOBQc1Mj4DNTQmNQ4DHhZA\ +SD4pBR4TEhwuODszJwYWQUg+KQEVVk1EAQFXGDRFZDchGTk2KykrLCVIPTo3MzkbqxkzRWU3BAwD\ +KV1DUwAAAAADAAD9KgE8AJEAGwApADcAABkBMx4GFRQHFhUUBxYVFAc2NTQuAiM1HgMXNDY1NC4C\ +IyceAxc0NjU0LgIjHgYnMzs4LhwSEhITHgU9V1UcCURNVhUBPFZVHQIJRE1WFQE8VlUd/pACARs5\ +Mzc6PUglLCsnLSwrKSs2ORkhRHdIKakkVEFdKQQNA0R2SCmrJFNCXSkEDQNEdkgpAAADAAD/VgE8\ +Ar0AHAApADYAABEzMj4DNTQnFhUUBxYVFAcWFRQOBQcjNzI+AjU0JjUOAycyPgI1NCY1DgMeFkBI\ +PikFHhMSEhIcLjg7MycGHh4dVVc9ARVWTUQJHVVXPQEVVk1EAVcYNEVkNyEZOTYrKSssLScrLCVI\ +PTo3MzkbqilJeEQEDAQpXUNUhilJeEQDDQQpXUNUAAQAAP1CATwBVAAgAC4APABKAAAZATMeBhUU\ +BxYVFAcWFRQHFhUUBzY1NC4DIzUeAxc0NjU0LgMnHgMXNDY1NC4DJx4DFzQ2NTQuAx4GJzM7OC4c\ +EhISEhITHgUpPkhAFglETVYVASk+SEEWCURNVhUBKT5IQRYJRE1WFQEpPkhB/qgCrBs5Mzc6PUgl\ +LCsnLSwrJy0sKykrNjkZITdkRTQYqyRUQ10pBAwEN2VFMxmrJFRDXSkDDgM3ZUUzGaskVENdKQQM\ +BDdlRTMZAAQAAP6OATwCoAAfACwAOQBGAAAZATMyPgI1NCcWFRQHFhUUBxYVFAcWFRQOBQc1Mj4C\ +NTQmNQ4DJzI+AjU0JjUOAycyPgI1NCY1DgMeHFVXPQUeExISEhISHC44OzMnBh1VVz0BFVZNRAkd\ +VVc9ARVWTUQJHVVXPQEVVk1E/o4CrClId0QhGTk2KykrLC0nKywtJyssJUg9OjczORuqKUl4RAMN\ +BCldQ1SGKUl4RAMNBCldQ1SGKUl4RAMNBCldQ1QAAAAFAAD9VQE8AhIAJAAyAEAATgBcAAAZATMe\ +BhUUBxYVFAcWFRQHFhUUBxYVFAc2NTQuAyM1HgMXNDY1NC4DJx4DFzQ2NTQuAyceAxc0NjU0LgMn\ +HgMXNDY1NC4DHgYnMzs4LhwSEhISEhISEx4FKT5IQBYJRE1WFQEpPkhBFglETVYVASk+SEEWCURN\ +VhUBKT5IQRYJRE1WFQEpPkhB/rsDVxs5Mzc6PUglLCsnLSwrJy0sKyctLCspKzY5GSE3ZEU0GKsk\ +VENdKQMNBDdlRTMZqyRUQ10pBAwEN2VFMxmrJFRDXSkDDgM3ZUUzGaskVENdKQQMBDdlRTMZAAAF\ +AAD9vAE8AnkAIwAwAD0ASgBXAAAZATMyPgI1NCcWFRQHFhUUBxYVFAcWFRQHFhUUDgUHNTI+AjU0\ +JjUOAycyPgI1NCY1DgMnMj4CNTQmNQ4DJzI+AjU0JjUOAx4cVVc9BR4TEhISEhISEhwuODszJwYd\ +VVc9ARVWTUQJHVVXPQEVVk1ECR1VVz0BFVZNRAkdVVc9ARVWTUT9vANXKUh3RCEZOTYrKSssLScr\ +LC0nKywtJyssJUg9OjczORuqKUl4RAMNBCldQ1SGKUl4RAMNBCldQ1SGKUl4RAMNBCldQ1SGKUl4\ +RAMNBCldQ1QAAAACAAD/ZADhAbAACgAWAAA3IgYdATY3NjU0JjcyFhUUBwYjETMRNmcUKyImKx0H\ +IzlLUkQoI3omE7kPODsvGyYmMiNJTFICTP68NAAAAgAA/oYAxQF6AAMADAAAFzc1BxEVNxEjNQcR\ +NxyQkKkZrAFlLZYtAUnoNP3A4jMCQwEAAAIAAP6YAP8BaAADAB8AADcVNzUDIzUHNTc1BzU3NTMV\ +NzUzFTcVBxU3FQcVIzUHU1paHjU1NTUeWh01NTU1HVpGpxun/jejD1wPpw9aD6ifHKujD1wPpw9a\ +D6ifHAAAAAEAFP+EAQsAegAeAAAXNSYnBzAVIzUzNycwIzUzFRYXNzA1MxUjBgcXMDMVwygMM0g5\ +MzM5SCESNEg5IRM0OXw7Jg0zO0oyMkg5IhEzOUciETRIAAQAAP9qAWwBsAAOABwAKwA6AAA3DgEd\ +ATI3Njc2NTQnJiM3MhYVFAcGBwYjETMRNhcOAR0BMjc2NzY1NCcmIzcyFhUUBwYHDgEjETMRNk4R\ +Hg4eHwwEChARGR0rCRgrNS8fGdERHRAeHQsGCxAPFh8qCxkoFjcWHht9AR4QxikrNA0ZHhQVJjkh\ +EiA5NEACRv7BMiYBHRHGKS8wExMcFhUmNiQWHD4vGyUCRv7BMgAAAQAA/wYAjAD6AA4AADcVBhUU\ +FxUuAzQ+AoxQUBolMhsbMiX6FEOjpkAUDx86WnBaOh8AAAEAAP8GAIwA+gAOAAA1HgMUDgIHNTY1\ +NCcaJTIbGzIlGlBQ+g8fOlpwWjofDxRApqNDAAACADL/ZAENAbAACgAWAAA3IgYVFBcWFzU0Jicy\ +FxEzESInJjU0NqsXIC4uGiozOiMjOlJPOXomGzBFQgPLEh4mNAFE/bRSUEUjMgAABP/1/2oBbAGw\ +AA4AHQAsADsAADcjIgcGFRQXFhcWMzU0JicyFxEzESImJyYnJjU0NhcOAR0BMjc2NzY1NCcmIzcy\ +FhUUBwYHDgEjETMRNlcBERAKBAwfHg4eKzAZHxY3FysYCSvpER0QHh0LBgsQDxYfKgsZKBY3Fh4b\ +fRUUHhkNNCspxhAeJzIBP/26JRs0OSASITkmAR0RxikvMBMTHBYVJjYkFhw+LxslAkb+wTIAAAAA\ +AQAA/sAAqQFAABMAABMzFTcVBxU3FQcVIzUHNTc1BzU3RB5HR0dHHkREREQBQKIOXA5/DloPqKIO\ +XA5/DloPAAMAAP6YAToBaAAjACcAKwAANzUzFTcVBxU3FQcVIzUHFSM1DwEjNQc1NzUHNTc1MxU3\ +NTMVAzUHFTcVNzXpHjMzMzMePR48AR4zMzMzHj0eHj1bPcCong9cD58PWg+1qhKspBCong9cD58P\ +Wg+1qhKspP79nhGfuJ4RnwAB//0AAAE/APQAGAAANwYjIiY1ND8BNi8BJjU0NjMyMRcFFhUUBxIC\ +AwcJBs8ODs0ICwcBAgEfDg4BARAICgNJBwZPAwsKEgFrBg4NBQAAAAEAAAAAAFAAUAAJAAA1NDYy\ +FhQGIyImFyIXFxEQGCgRFxciFxgAAAABAAAAAAFUADIAAwAAMTUhFQFUMjIAAAABAAAAAABkARgA\ +AwAAMwMzAygoZCgBGP7oAAAAAQAAAAABGAE1AAUAADEbASMnB4yMQVhaATX+y8bGAAACAAAAAAJY\ +AUoADgAZAAAxNDYzMh4CFSMuASIGByEiJjQ2MzIWFRQGs3k5a1UzDwui4KILARwXJSUXGSMjmLIs\ +UYBNboaGbiQwJCQYGSMAAAEAAAAAALYBLQAYAAATMhYXFhUUBw4BIzAnJjU0NjU0Iy4BNTQ2Vhsb\ +EBoyGUQQBgFHFBsoLQEtDBEdMD08HS0DAQIIaxMPASYcHjEAAAAAAgAFAAABjgH1ABEAIwAANxM2\ +MzIXFhUUBwMGIyInJjU0JxM2MzIXFhUUBwMGIyInJjU0mM0LCgYDCwXMCQsEBguLzQoLBgMLBcwI\ +DAQGCyMBwBICBQoGDP4/EQMGDAcHAcASAgUKBgz+PxEDBgwHAAEAAP8GAIIA+gADAAA1MxEjgoL6\ +/gwAAAEAAAAAAIIA+gADAAA1MxUjgoL6+gAAAAEAAP+DASwAAAADAAAxIRUhASz+1H0AAAEAAAAA\ +ASwAfQADAAA1IRUhASz+1H19AAEAAP5+AOsBhwATAAATFwcXJiMiBhUUFyY1NDYzMhcnNym9Z2wy\ +NB8mOHg0JSIih2QBh+XZzy4kHTU0S00jLRW8tAAAAQAA/w0BAADAABYAADcOAiMiJjU0NjIWFRQH\ +MjY3NjIXAyerAxkaEys3JjgpFyIzIQIVA5YwPAEHBCkoHyAeGR0bISwCAv5vEAAAAAEAAP4MAUgA\ +wAAkAAAXBiMiJjU0NjMyFhUUBzI/AQYjIiY1NDYzMhYVFAcyNzYyFwMnqyghKzcnGxwpF0ELPDYY\ +KzcnGxwpF0guAhUDxS3EDCgoICAfGR0bIsoMKSgfIB4ZHRtNAgL9bgwAAAEAAP4MAY8BwAA2AAA3\ +BiMiJjU0NjMyFhUUBzI/ASIOASMiJjU0NjMyFhUUBzI3NjIXAScTBiMiJjU0NjMyFhUUBzI39igf\ +KzcnGxwpFz8LOgEgHBMrNycbHCkXSC4BFgP+9C1VKCErNycbHCkXQQs8DCgoICAfGR0bIssJBCko\ +HyAeGR0bTQIC/G4MASQMKCggIB8ZHRsiAAAAAAEAAP0MAdoBwABFAAATBiMiJjU0NjMyFhUUBzI/\ +AQYjIiY1NDYzMhYVFAcyPwEGIyImNTQ2MzIWFRQHMj8BIg4BIyImNTQ2MhYVFAcyNzYyFwEnqygh\ +KzcnGxwpF0ELOighKzcnGxwpF0ELOigfKzcnGxwpFz8LOgEgHBMrNyY4KRdILgIVA/6pLf48DCgo\ +ICAfGR0bIsoMKCggIB8ZHRsiygwoKCAgHxkdGyLLCQQpKB8gHhkdG00CAvtuDAAAAAEAAP0MAhkC\ +rgBWAAAlBiMiJjU0NjMyFhUUBzI/AQYjIiY1NDYzMhYVFAcyPwEiDgEjIiY1NDYzMhYVFAcyNzYy\ +FwEnEwYjIiY1NDYzMhYVFAcyPwEGIyImNTQ2MzIWFRQHMjcBPyghKzcnGxwpF0ELNigfKzcnGxwp\ +Fz8LNAEgHBMrNycbHCkXSC4BFgP+ai1VKCErNycbHCkXQQs6KCErNycbHCkXQQs0DCgoICAfGR0b\ +IsQMKCggIB8ZHRsixwkEKSgfIB4ZHRtNAgL6gAwBJAwoKCAgHxkdGyLIDCgoICAfGR0bIgAD//D/\ +BgImAPoABwAPABMAADYiJjQ2MhYUACImNDYyFhQFATMBUDIjIzIjAYgyIyMyI/3SAbh+/kdLIzIj\ +IzL+zyMyIyMyWgH0/gwABP/h/wYDBwD6AAcADwATABcAADYiJjQ2MhYUACImNDYyFhQFATMBMwEz\ +AUEyIyMyIwJ3MiMjMiP84wG4e/5HeQG4e/5HSyMyIyMy/s8jMiMjMloB9P4MAfT+DAAC/7T/iAF8\ +ARgAEQA7AAA3FjMyNjc2NTQnJiMiBgcGFRQXIicHMzIUKwEiNDsBEzY1NCMiDgMHBiY3Njc2MzIW\ +Fz4BMzIWFRQGxQIFEjIODQ8CBBI1Cw8nKhktNAsL4QsLS2gGCwgMDwsZCgUbBTEPFyUjJAcdJiMe\ +LVkoATUkICYlBQEzHCckKS4geh4eAR0SDA8HFBIsEAgPCVgQGRMaHg80MElrAAAB/9v/9gG+ARgA\ +UwAANwYHBisBIj8BNiYjIgYHBiY3PgMzMhc2MzIXPgEzMhYVFA8BBhUUMzI3PgU3NhYHDgIjIiY1\ +ND8BNjU0IyIPAQYHJwYmPwE2NTQjIgdQBwgEBDUNDUIEBggNFiQFFQQUECIfEjcLJCQtCQspExkk\ +BS8ECAEEBQsHDAMNAQYVBhIXLR8VGQU0ARUbCEEIDyYNCARDARUbCBURAwEapw0PGjkIDAklGjAS\ +KCgoEBgjGgsPfQsJDgIDCQYPBRIBCQ0LHx8aFhMNDYgCBA4UqxYBAQEPCKsCBA4UAAAAAf9+/2AB\ +XgG4AEEAAAciJjU0NjMyFhUUBwYVFDMyPgc3IyI1NDsBPgEzMhYVFAYjIiY1NDc2NCMiDgcHMzIU\ +KwEOATIgMBcTEhcSChkLEA8LDQoODRQKNRMRQRRpNCAwFxMSFxIKGQcMCgcIBQYDBgE2ExM/IXag\ +JiAaIhQPDgsHDQ4GERMlJDw3VScVE0tfJiAaIhQPDgsGHAULChQNGg0eBijFwQAAAf/bAAABEwET\ +ACoAADc+ASYjIgYPAQYHBisBIj8BNiYjIgYHBiY3PgEzMhYXNjMyFhUUIyImNTTQBQMDBRQkCT8H\ +CAQENQ0NQgQGCA4XIgUVBCE4IhsdBB8kGiArDxvnAwcELBicEQMBGqMNDxo1CAwJPEAaDSceGTcW\ +DRQAAAABAAAAAADcARgAMQAAMyImNTQ2MhYVFAcWMzI2NTQuAicmNTQ2MzIWFRQGIyImNTQ3JiMi\ +BhUUHgIXFhUUUB4yFRoXEAwTFiEJCxgGOjctIjYWEA0WBw8RDxkREh4FMCsbEBYQDBIQEBYSCw8H\ +DQQlMCMoJBgQGBQOAw4TEQ0JEwwRAyArVQAAAf/k//wA5wEPADwAACcGLgE/ASIGIyImDgEHBicm\ +Nz4BNx4BMzI2MzIXFhQPAQYVFDMyNjMyFxY3NiciNTQ2MzIVFAYrAS4BIyIKBgsBBbAFHwwDFAwf\ +BQ8EAwgMCwEOLxUiKwQIBQsHngICAQ0GGiMNCAUHJBINJigeHRglCA4DBAgNBsEGBgIyBQ0LCRIe\ +MAIBBgsBAREHpwYCAwMaCg4NAx0OFTceLwMYAAX/tP+IBUsBGAAQACIAtQDGANcAACUWMzI2NzY0\ +JyYjIgYHBhUUBRYzMjY3NjU0JyYjIgYHBhUUFyInBzMyFCsBIjQ7ARM2NTQjIg4DBwYmNzY3NjMy\ +Fhc+ATMyFz4BMzIWFz4BMzIXPgEzMhYXPgEzMhc+ATMyFhc+ATMyFhUUBiMiJwczMhQrASI0OwET\ +NjU0IyIGBxUUBiMiJwczMhQrASI0OwETNjU0IyIGBxUUBiMiJwczMhQrASI0OwETNjU0IyIGBxUU\ +BiUWMzI2NzY0JyYjIgYHBhUUBRYzMjY3NjQnJiMiBgcGFRQCCgIFEjIODQ8CBBI1Cw/+ywIFEjIO\ +DQ8CBBI1Cw8TFhktNAsL4QsLS2gGCwgMDwsZCgUbBTEPFyUjJAcdJiMvExIiGyMkBx0mIy8TEiIb\ +IyQHHSYjLxMSIhsjJAcdJiMeLWtJFhktNAsL4QsLS2gGCw4UFGtJFhktNAsL4QsLS2gGCw4UFGtJ\ +FhktNAsL4QsLS2gGCw4UFGsDgwIFEjIODQ8CBBI1Cw/+ywIFEjIODQ8CBBI1Cw8oATUkIUoFATMc\ +JyQpBgE1JCAmJQUBMxwnJCkuIHoeHgEdEgwPBxQSLBAIDwlYEBkTGh4PMhwWExoeDzIcFhMaHg8y\ +HBYTGh4PNDBHbSB6Hh4BHRIMDxgjAUdtIHoeHgEdEgwPGCMBR20geh4eAR0SDA8YIwFHbSgBNSQh\ +SgUBMxwnJCkGATUkIUoFATMcJyQpAAT/tP+IBAYBGABvAIAAkQCjAAAhIicHMzIUKwEiNDsBEzY1\ +NCMiBgcVFAYjIicHMzIUKwEiNDsBEzY1NCMiBgcVFAYjIicHMzIUKwEiNDsBEzY1NCMiDgMHBiY3\ +Njc2MzIWFz4BMzIXPgEzMhYXPgEzMhc+ATMyFhc+ATMyFhUUBicWMzI2NzY0JyYjIgYHBhUUBRYz\ +MjY3NjQnJiMiBgcGFRQFFjMyNjc2NTQnJiMiBgcGFRQDUhYZLTQLC+ELC0toBgsOFBRrSRYZLTQL\ +C+ELC0toBgsOFBRrSRYZLTQLC+ELC0toBgsIDA8LGQoFGwUxDxclIyQHHSYjLxMSIhsjJAcdJiMv\ +ExIiGyMkBx0mIx4ta0wCBRIyDg0PAgQSNQsP/ssCBRIyDg0PAgQSNQsP/ssCBRIyDg0PAgQSNQsP\ +IHoeHgEdEgwPGCMBR20geh4eAR0SDA8YIwFHbSB6Hh4BHRIMDwcUEiwQCA8JWBAZExoeDzIcFhMa\ +Hg8yHBYTGh4PNDBHbSgBNSQhSgUBMxwnJCkGATUkIUoFATMcJyQpBgE1JCAmJQUBMxwnJCkAAAP/\ +tP+IAsEBGABMAF4AbwAAMyInBzMyFCsBIjQ7ARM2NTQjIg4DBwYmNzY3NjMyFhc+ATMyFz4BMzIW\ +Fz4BMzIWFRQGIyInBzMyFCsBIjQ7ARM2NTQjIgYHFRQGJxYzMjY3NjU0JyYjIgYHBhUUBRYzMjY3\ +NjQnJiMiBgcGFRTIFhktNAsL4QsLS2gGCwgMDwsZCgUbBTEPFyUjJAcdJiMvExIiGyMkBx0mIx4t\ +a0kWGS00CwvhCwtLaAYLDhQUa0wCBRIyDg0PAgQSNQsPAVUCBRIyDg0PAgQSNQsPIHoeHgEdEgwP\ +BxQSLBAIDwlYEBkTGh4PMhwWExoeDzQwR20geh4eAR0SDA8YIwFHbSgBNSQgJiUFATMcJyQpBgE1\ +JCFKBQEzHCckKQAAAAL/2/+IAysBGAByAIMAACU2NzYzMhYXPgEzMhYVFAYjIicHMzIUKwEiNDsB\ +EzY1NCMiDgMHDgEjIiY1ND8BNjU0IyIPAQYHJwYmPwE2NTQjIg8BBgcGKwEiPwE2JiMiBgcGJjc+\ +AzMyFzYzMhc+ATMyFhUUDwEGFRQzMjc+ARcWMzI2NzY0JyYjIgYHBhUUAa0xFBgiIyQHHSYjHi1r\ +SRYZLTQLC+ELC0toBgsHDxIOGAgaPC0VGQU0ARUbCEEIDyYNCARDARUbCEQHCAQENQ0NQgQGCA0W\ +JAUVBBQQIh8SNwskJC0JCykTGSQFLwQIAQQPJdECBRIyDg0PAgQSNQsPd2wYHRMaHg80MEdtIHoe\ +HgEdEgwPDR8bNRA2OBYTDQ2IAgQOFKsWAQEBDwirAgQOFKwRAwEapw0PGjkIDAklGjASKCgoEBgj\ +GgsPfQsJDgIINTkBNSQhSgUBMxwnJCkAAAAAAv/b/2ADGQG4AFMAlQAANwYHBisBIj8BNiYjIgYH\ +BiY3PgMzMhc2MzIXPgEzMhYVFA8BBhUUMzI3PgU3NhYHDgIjIiY1ND8BNjU0IyIPAQYHJwYmPwE2\ +NTQjIgcTIiY1NDYzMhYVFAcGFRQzMj4HNyMiNTQ7AT4BMzIWFRQGIyImNTQ3NjQjIg4HBzMyFCsB\ +DgFQBwgEBDUNDUIEBggNFiQFFQQUECIfEjcLJCQtCQspExkkBS8ECAEEBQsHDAMNAQYVBhIXLR8V\ +GQU0ARUbCEEIDyYNCARDARUbCPUgMBcTEhcSChkLEA8LDQoODRQKNRMRQRRpNCAwFxMSFxIKGQcM\ +CgcIBQYDBgE2FBQ/IXYVEQMBGqcNDxo5CAwJJRowEigoKBAYIxoLD30LCQ4CAwkGDwUSAQkNCx8f\ +GhYTDQ2IAgQOFKsWAQEBDwirAgQOFP6fJiAaIhQPDgsHDQ4GERMlJDw3VScVE0tfJiAaIhQPDgsG\ +HAULChQNGg0eBijFwQAAAAH/fv9gAmkBuAB0AAAlIw4BIyImNTQ2MzIWFRQHBhUUMzI+BzcjIjU0\ +OwE+ATMyFhUUBiMiJjU0NzY0IyIHBgcXPgEzMhYVFAYjIiY1NDc2NCMiDgcHMzIUKwEOASMiJjU0\ +NjMyFhUUBwYVFDMyPgcBX5shdl8gMBcTEhcSChkLEA8LDQoODRQKNRMRQRRpNCAwFxMSFxIKGSYX\ +AwGbFGk0IDAXExIXEgoZBwwKBwgFBgMGATYUFD8hdl8gMBcTEhcSChkLEA8LDQoPDRTmxcEmIBoi\ +FA8OCwcNDgYREyUkPDdVJxUTS18mIBoiFA8OCwYccwwGAUtfJiAaIhQPDgsGHAULChQNGg0eBijF\ +wSYgGiIUDw4LBw0OBhETJSQ8N1UAAAAB/37/YAN0AbgAqgAAEzM+ATMyFhUUBiMiJjU0NzY0IyIH\ +BgcXPgEzMhYVFAYjIiY1NDc2NCMiDgcHMzIUKwEOASMiJjU0NjMyFhUUBwYVFDMyPgc3Iw4BIyIm\ +NTQ2MzIWFRQHBhUUMzI+BzcjDgEjIiY1NDYzMhYVFAcGFRQzMj4HNyMiNTQ7AT4BMzIWFRQGIyIm\ +NTQ3NjQjIg4CBwbMnBRpNCAwFxMSFxIKGSYXAwGbFGk0IDAXExIXEgoZBwwKBwgFBgMGATYTEz8h\ +dl8gMBcTEhcSChkLEA8LDQoPDRQKmyF2XyAwFxMSFxIKGQsQDwsNCg4NFQqbIXZfIDAXExIXEgoZ\ +CxAPCw0KDg0UCjUTEUEUaTQgMBcTEhcSChkOFQ4IBgIBDktfJiAaIhQPDgsGHHMMBgFLXyYgGiIU\ +Dw4LBhwFCwoUDRoNHgYoxcEmIBoiFA8OCwcNDgYREyUkPDdVJ8XBJiAaIhQPDgsHDQ4GERMlJDs4\ +VSfFwSYgGiIUDw4LBw0OBhETJSQ8N1UnFRNLXyYgGiIUDw4LBhwTJyIbCgAB/37/YASAAbgA4AAA\ +ARc+ATMyFhUUBiMiJjU0NzY0IyIHBgcXPgEzMhYVFAYjIiY1NDc2NCMiDgcHMzIUKwEOASMiJjU0\ +NjMyFhUUBwYVFDMyPgc3Iw4BIyImNTQ2MzIWFRQHBhUUMzI+BzcjDgEjIiY1NDYzMhYVFAcGFRQz\ +Mj4HNyMOASMiJjU0NjMyFhUUBwYVFDMyPgc3IyI1NDsBPgEzMhYVFAYjIiY1NDc2NCMiBwYHFz4B\ +MzIWFRQGIyImNTQ3NjQjIg4HAdicFGk0IDAXExIXEgoZJhcDAZsUaTQgMBcTEhcSChkHDAoHCAUG\ +AwYBNhMTPyF2XyAwFxMSFxIKGQsQDwsNCg8NFAqbIXZfIDAXExIXEgoZCxAPCw0KDg0UCpshdl8g\ +MBcTEhcSChkLEA8LDQoPDRQKmyF2XyAwFxMSFxIKGQsQDwsNCg4NFAo1ExFBFGk0IDAXExIXEgoZ\ +JhcDAZsUaTQgMBcTEhcSChkHDAoHCAUGAwYBDwFLXyYgGiIUDw4LBhxzDAYBS18mIBoiFA8OCwYc\ +BQsKFA0aDR4GKMXBJiAaIhQPDgsHDQ4GERMlJDw3VSfFwSYgGiIUDw4LBw0OBhETJSM8N1YnxcEm\ +IBoiFA8OCwcNDgYREyUkPDdVJ8XBJiAaIhQPDgsHDQ4GERMlJDw3VScVE0tfJiAaIhQPDgsGHHMM\ +BgFLXyYgGiIUDw4LBhwFCgsTDhkOHQAAAAADAAD/YALfAbgANgBxALYAADMiJjU0NjMyFhUUBxYz\ +MjY1NC4DJy4CNTQ2MzIWFRQGIyImNTQ3JiMiBhUUHgIXHgEVFCUGLgE/ASYjIgYjIg4BBwYnJjc+\ +ATcWMzI2MzIXFhQPAQYVFB4BFxY+AScuATU0NjMyFRQGKwEuASMiBSImNTQ2MzIWFRQHDgEeARUU\ +FjI+BzcjIjU0OwE+ATMyFhUUBiMiJjU0NzY0IyIOBwczMhQrAQ4BUB4yEgwOFwwGGBYhAwwFFwMU\ +GBQ3LSI2FhANFgUQDg8ZERIeBRsXASYGCwEFrgoUBRoJBRASBRAFAwcLDAExGiAoBwIUCweaBBoq\ +DQUJBQIDJBQMJisgBhc1CRP+tCAwFxMSFxIEAQECBhYQDwsNCg4NFAo1ExFBFGk0IDAXExIXEgoZ\ +BwwKBwgFBgMGATYTEz8hdisbEBYNCxgMEhYSCQwNBA4CDRIeDyMtJBgQGBQOBgoUEQ0JEwwRAxIf\ +FVoDBAgNBr8FARgfBA0LCBMeLgQDCAIBEQejBQUHAwcLAwQNBggIFAsTNx4qAg2yJiAaIhQPDgsC\ +BQQGAwgGBhETJSQ8N1UnFRNLXyYgGiIUDw4LBhwFCwoUDRoNHgYoxcEAAgAF//sB/AGaAAkALAAA\ +AQ8BBhUUMzI2NwcOASMiJjU0PwEjNTM/AQc3MhU2MzIWFRQGIiY1NDcGDwEjATZ/OQIUGEQSDCky\ +HyIiAzdpcxZZJpgZIDUYHRggFgk3EEJIAQkHuAgDFRcPKBkUJBoLC7MgSi54Ci0pHBUSGxEOEhMP\ +J9gAAQAA//0BtQDUAC8AACUyNTQnBiImNT4BMzIWFRQHBiMiLwEmIyIVFBc2MzIWFRQGByInJjU0\ +NzYzFh8BFgFwLRoQHBQBFwkkLSkXHiYeohoRLhkQDw0VFA0dGBwoFiIqF6IcMTkgExAWDQ4WMzUz\ +JBUVehI6IBIQFw4PEgIaIi0zJBMCEHoTAAEAAP/NAbUBAwA2AAAXIiY1NDc2MxYfATUzFRcWMzI2\ +NTQnBiMiNT4BMzIWFRQHBiMiLwEVIzUnJiMiBhUUFzYzMhUUTSAtKBYgKBcuHlwiDxUcFhQSHAEP\ +CSAtKRccJB4uHlwgDxYcFRQTHANDJjMkEwIQJGmARxcpHCcQDBsPFUImMyQVFSRsg0cWKB4nDwwd\ +IAABAA0AAAJFAOAACwAANyc3FzcXNxcHJwcnIhWOZXhqTRaSaXRpLhmZfHx8VBehfHx8AAAAAQAN\ +/8sCRQERABMAACUHJwcnNxc3NTMXNxc3FwcnBxUjARZAaUsVjmUWGwFGak8UkmkZG0ZGfE4ZmXwX\ +lntKfFUYoXwblgAAAQAAAAABGAEYAAsAADM1IzUzNTMVMxUjFXt7eyJ7e3sie3siewAAAAEAAAAA\ +ATYBcgAKAAAxNT4ENzMUBiQxTDQ0Dx7APAcPKz1vSY7aAAAB//8AAAEtAKAAHQAANz4CMzIeARcW\ +MzI3NhYHDgIjIi4BJyYjIgcGJgEJECkcGCYmDwkKHhgEDgIIESkcGCQlEgcIHR0FDUYZISAhMQoG\ +JAYHBxkiHyExCgQjBggAAAABAAAAAAEsASwABwAAMREhESM1IxUBLCPmASz+1LS0AAEAAAAAAPoB\ +wgAGAAAzAzMbATMDaWkoVVUoaQHC/pgBaP4+AAIAAAAAAMgAyAAHAA8AADYyNjQmIgYUFiImNDYy\ +FhRFPiwsPix0Ujs7UjsZLD4sLD5FO1I7O1IAAf84AAAAyADIAAsAACM0NjIWFSM0JiIGFch2pHYe\ +YJRgUnZ2UkpgYEoAAAACAAAAAAC0ASwABwAVAAA2MjY0JiIGFBc1LgE1NDYyFhUUBgcVSx4bGx4b\ +GB0rN0Y3Kx14NTY1NTatZAg1Jyg8PCgnNQhkAAACAAAAAADIASwADwAfAAA3LgE1NDYyFhUUBgcd\ +ASM1Nz4BNTQmIgYVFBYXPQEzFVQkMDtSOzAkICAZIiw+LCIZIGYFOCUpOzspJTgFAWVlGQYpGx8s\ +LB8bKQYBSUkAAAAEAAD//AP0An8AgwCNAJkAowAANz4BNTQnLgE1ND4CPwIOARUUMzI3Fw4BIyIm\ +NTQ+AjMyFhUUBiMiJic3HgEzMjU0LgInBwYVFB4CFRQGDwEeAjsBMjcmNTQ3NjMyFhUUBwYHHgEz\ +MjY1NDY3LgI9AR4BFRQGIyInBiMiLgEnDgEiJicmIyIOAQcGIyI1NDYFIiY0NjMyFhQGJT4BNTQn\ +JiMiBhUUBTY1NCYnDgEVFJg2RQUDUgMEBQECPWJqIB4dGhUpJR4tHz5wSH1yMi4cNhIYERQUMwwc\ +QC0bCR0iHR8QEA4pHwwNESIhHCJMGyMKHTwTIBsWMUNdIIFeurRRQkEjJDwhMxYOLCggGxwaEgoU\ +GQZaIQowA1wLExMLDBIR/g8zJAQFFRwuATxaFxI/MFoYPyAKDAh0IgUODgwEBKUEYywjPA1CLyYa\ +IUdELFE3MDUwKw4eETIKGiIaAkIkGx42ISwVJk8UFAopGh41KDUlLisgIRM5MBcSKhlSXxksWTQG\ +ATyxa0ZfOToVFBImGRYgLQsVBDoHDThPEhgSEhgSgC8vGwcOEjkrHnQFgSJJFR1YRD8AAAIADAAK\ +AdMBzwAKAI8AACU0JiMiBhQWMzI2Jw4BIyImNDYzMhYXNjU0JyYjIiY0NjMyFx4BFxYzMjU0Jy4B\ +NTQ2MzIWFRQGBxQzMjc+ATc2MzIWFRQGIyIGBwYVFDMyNjMyFhQGIyImIyIGFRQXHgEXFhUUBiMi\ +Jy4BJyYjIhUUFhUUBiMiJjQ2NTQjIgcOAQcGIyImNTQ3NjMyNzY1NAEWGA8QFRYPEBeJGSYOGRsa\ +GQ0qGCUMFBgdHBkXEhALAxQLEhQBAiceFBIbIwEWEA0RAQ0MGhMeGxIbFQ0QIRsrDhwbHRkPJxQX\ +EgsUOA0OGRcTFAwBExEKEiogExIdJhcNDhIDERIMFBoNDBYjEg3uEBQTIhYVCgElGSobJQIDFg4K\ +ExwoHQwLPhYNGA0IFycPFxobFhEjGSoQEzgPDhsUESMGCw4PFicdKBomCAoWChIDCw0ZExsOCzkV\ +Dx8eMBMUGBokMRckDhI9DAcWFBgODRINDBoAAAADAAD/BgH0APoABwAPABcAADYUFjI2NCYiAjQ2\ +MhYUBiI2IiY0NjIWFC14qnh4qqWS0JKS0IEyIyMyI1WqeHiqeP7L0JKS0JK+IzIjIzIAAAIAAP8G\ +AfQA+gAHAA8AADYUFjI2NCYiAjQ2MhYUBiIteKp4eKqlktCSktBVqnh4qnj+y9CSktCSAAAAAAMA\ +AP6iAfQBXgARABcAHQAAEzMVHgEVFAYHFSM1LgE1NDY3GQEOARQWFz4BNCYn5C1gg4NgLWCEg2FO\ +aWl7TWlpTQFeZAqOYmGPCmRlCI9iY44I/jsBmAh1nnUICHacdggAAAACAAD/BgHSAPoAIQApAAAl\ +FhUUBwYjIicmIyIGFBYzMjc2MzIXFhUUBwYjIiY0NjMyAiImNDYyFhQBzwILBQYNCjN3VXh4VXgy\ +BhIHAwwDQJVokpJolHsyIyMyI4IGBA8GAw5feKp4Xw0CBwwGBniS0JL+yiMyIyMyAAAAAQAA/wYB\ +0gD6ACEAACUWFRQHBiMiJyYjIgYUFjMyNzYzMhcWFRQHBiMiJjQ2MzIBzwILBQYNCjN3VXh4VXgy\ +BhIHAwwDQJVokpJolIIGBA8GAw5feKp4Xw0CBwwGBniS0JIAAAIAAP6iAdIBXgAkACoAACUWFxYV\ +FAcGIyInJicRNjc2MzIXFhUUBwYHFSM1LgE1NDY3NTMDEQ4BFBYBEYQ6AgsFBg0KLWZlLgYSBwMM\ +AzyCLWCEg2EtLU5pafkKbQYEDwYDDlQK/mgHVw0CBwwGBnAHZWUIj2Jjjghl/dYBmAh1nnUAAAEA\ +AP/EAHgAPAAHAAAWIiY0NjIWFFUyIyMyIzwjMiMjMgAAAAEAAP6iAC0BXgADAAATESMRLS0BXv1E\ +ArwAAAACAAD9EgFKAL4AAwAPAAA3FSE1JTMVITUzESMRIRUjHgEO/tQeAQ4eHv7yHkaMjHgyMvxU\ +AmIyAAEAAAAAAlYBcgALAAA1Nxc3FzcXAScHJweJVFZSryL+/FRWUzVBuXNzcekW/qR0dHBHAAAB\ +/+gAvQESATcAFwAAJj4BMzIWMjc2MzIVFA4BIyImIgcGIyI1GDAhHBRVKBQDBw4wIRwUVSgUBAcN\ +9jARPRQDBwwwET0UBAgAAAAAAgAA/4kBwgCLAAsAEwAABTQmIyIGFRQWMzI+ARQGIiY0NjIBUVo2\ +IDRdNyEvcX3IfX3IFDRPJx80TCN0bEtLbEsAAAACAAD/bAFIAqgADQAcAAAlJiMiBhUUFxYzMjY1\ +NBMzERQGIyInJjU0NjMyFwEkDSU8lwYLJjyXAh1+SE8kD35IQCREF2ErCgkXYSsJAm79bkNnQx4d\ +Q2ctAAAAAAEAAP95AUACqAANAAABMxEUBiMiJjU0NjMyFwEiHnlZMjx6WDEfAqj9gEZpOCdFahwA\ +AAABAAD/eQIKAqgAGgAAAREUBiMiJjU0NjMyFxE1Mx4EFRQHNjU0AUB5WTI8elgxHx4GLjk4JTIS\ +Abn+b0ZpOCdFahwBTu81XEdOdUlIckFJ7wAAAAACAAD/eQILAqgAHwAoAAAlNREzHgQVFAcWFRQH\ +NjU0JiMRFAYjIiY1NDYzMhMeARc0NjU0JgEiHgkwODYjFRYiBXY4eVkyPHpYMT0OhRsBdmvmAVck\ +RztCWDMlMjAkLkEZIV+e/tZGaTgnRWoBdjuiNQMOA1+fAAABAAD/zgBkADIABwAAFiImNDYyFhRH\ +Kh0dKh0yHSodHSoAAAAAAA4ArgABAAAAAAAAAIMBCAABAAAAAAABAAcBnAABAAAAAAACAAcBtAAB\ +AAAAAAADAB8B/AABAAAAAAAEAAcCLAABAAAAAAAFAAgCRgABAAAAAAAGAAcCXwADAAEECQAAAQYA\ +AAADAAEECQABAA4BjAADAAEECQACAA4BpAADAAEECQADAD4BvAADAAEECQAEAA4CHAADAAEECQAF\ +ABACNAADAAEECQAGAA4CTwBDAG8AcAB5AHIAaQBnAGgAdAAgAFwAMgA1ADEAIAAyADAAMQA4AC0A\ +MgAwADEAOQAgAEoAZQBhAG4ALQBGAHIAYQBuAGMAbwBpAHMAIABNAG8AaQBuAGUALgAgAFQAaABp\ +AHMAIABmAG8AbgB0ACAAaQBzACAAbABpAGMAZQBuAHMAZQBkACAAdQBuAGQAZQByACAAdABoAGUA\ +IABTAEkATAAgAE8AcABlAG4AIABGAG8AbgB0ACAATABpAGMAZQBuAHMAZQAgAFwAKABoAHQAdABw\ +ADoALwAvAHMAYwByAGkAcAB0AHMALgBzAGkAbAAuAG8AcgBnAC8ATwBGAEwAXAApAC4AAENvcHly\ +aWdodCBcMjUxIDIwMTgtMjAxOSBKZWFuLUZyYW5jb2lzIE1vaW5lLiBUaGlzIGZvbnQgaXMgbGlj\ +ZW5zZWQgdW5kZXIgdGhlIFNJTCBPcGVuIEZvbnQgTGljZW5zZSBcKGh0dHA6Ly9zY3JpcHRzLnNp\ +bC5vcmcvT0ZMXCkuAABhAGIAYwAyAHMAdgBnAABhYmMyc3ZnAABSAGUAZwB1AGwAYQByAABSZWd1\ +bGFyAABGAG8AbgB0AEYAbwByAGcAZQAgADoAIABhAGIAYwAyAHMAdgBnACAAOgAgADEANAAtADQA\ +LQAyADAAMgAzAABGb250Rm9yZ2UgOiBhYmMyc3ZnIDogMTQtNC0yMDIzAABhAGIAYwAyAHMAdgBn\ +AABhYmMyc3ZnAABWAGUAcgBzAGkAbwBuACAAAFZlcnNpb24gAABhAGIAYwAyAHMAdgBnAABhYmMy\ +c3ZnAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJIAAAABAAIBAgADAQMBBAEF\ +AQYBBwEIAQkBCgELAQwBDQEOAQ8BEAERARIBEwEUARUBFgEXARgBGQEaARsBHAEdAR4BHwEgASEB\ +IgEjASQBJQEmAScBKAEpASoBKwEsAS0BLgEvATABMQEyATMBNAE1ATYBNwE4ATkBOgE7ATwBPQE+\ +AT8BQAFBAUIBQwFEAUUBRgFHAUgBSQFKAUsBTAFNAU4BTwFQAVEBUgFTAVQBVQFWAVcBWAFZAVoB\ +WwFcAV0BXgFfAWABYQFiAWMBZAFlAWYBZwFoAWkBagFrAWwBbQFuAW8BcAFxAXIBcwF0AXUBdgF3\ +AXgBeQF6AXsBfAF9AX4BfwGAAYEBggGDAYQBhQGGAYcBiAGJAYoBiwGMAY0BjgGPBi5ub2RlZgd1\ +bmlFMDAwB3VuaUUwMzAHdW5pRTAzOAd1bmlFMDM5B3VuaUUwNDMHdW5pRTA0NQd1bmlFMDQ2B3Vu\ +aUUwNDcHdW5pRTA0OAd1bmlFMDUwB3VuaUUwNUMHdW5pRTA2Mgd1bmlFMDY5B3VuaUUwN0EHdW5p\ +RTA3Qgd1bmlFMDdDB3VuaUUwN0QHdW5pRTA3RQd1bmlFMDgwB3VuaUUwODEHdW5pRTA4Mgd1bmlF\ +MDgzB3VuaUUwODQHdW5pRTA4NQd1bmlFMDg2B3VuaUUwODcHdW5pRTA4OAd1bmlFMDg5B3VuaUUw\ +OEEHdW5pRTA4Qgd1bmlFMDhDB3VuaUUwOTQHdW5pRTA5NQd1bmlFMEEwB3VuaUUwQTEHdW5pRTBB\ +Mgd1bmlFMEEzB3VuaUUwQTQHdW5pRTBBOQd1bmlFMEIzB3VuaUUxMDEHdW5pRTFCOQd1bmlFMUJC\ +B3VuaUUxRTcHdW5pRTFGMgd1bmlFMUY0B3VuaUUxRjcHdW5pRTFGOQd1bmlFMUZDB3VuaUUxRkUH\ +dW5pRTFGRgd1bmlFMjAwB3VuaUUyNDAHdW5pRTI0MQd1bmlFMjQyB3VuaUUyNDMHdW5pRTI0NAd1\ +bmlFMjQ1B3VuaUUyNDYHdW5pRTI0Nwd1bmlFMjQ4B3VuaUUyNDkHdW5pRTI2MAd1bmlFMjYxB3Vu\ +aUUyNjIHdW5pRTI2Mwd1bmlFMjY0B3VuaUUyNmEHdW5pRTI2Ygd1bmlFMjgwB3VuaUUyODEHdW5p\ +RTI4Mgd1bmlFMjgzB3VuaUU0QTAHdW5pRTRBMgd1bmlFNEE0B3VuaUU0QTgHdW5pRTRBQwd1bmlF\ +NEMwB3VuaUU0Q0UHdW5pRTREMQd1bmlFNEUxB3VuaUU0RTIHdW5pRTRFMwd1bmlFNEU0B3VuaUU0\ +RTUHdW5pRTRFNgd1bmlFNEU3B3VuaUU0RTgHdW5pRTRFOQd1bmlFNEVBB3VuaUU1MDAHdW5pRTUw\ +MQd1bmlFNTIwB3VuaUU1MjEHdW5pRTUyMgd1bmlFNTIzB3VuaUU1MjQHdW5pRTUyNQd1bmlFNTI5\ +B3VuaUU1MkEHdW5pRTUyQgd1bmlFNTJDB3VuaUU1MkQHdW5pRTUyRgd1bmlFNTMwB3VuaUU1MzEH\ +dW5pRTUzOQd1bmlFNTY2B3VuaUU1NjcHdW5pRTU2OQd1bmlFNTZDB3VuaUU1NkQHdW5pRTU4Mgd1\ +bmlFNUQwB3VuaUU1RTIHdW5pRTYxMAd1bmlFNjEyB3VuaUU2MTQHdW5pRTYxOAd1bmlFNjI0B3Vu\ +aUU2MzAHdW5pRTY1MAd1bmlFNjU1B3VuaUU5MTAHdW5pRTkxMQd1bmlFOTEyB3VuaUU5MTQHdW5p\ +RTkxNQd1bmlFOTE4B3VuaUU5MjAHdW5pRTkyNQd1bmlFOTVEB3VuaUVBMDIHdW5pRUFBNAd1bmlF\ +Q0EyB3VuaUVDQTMHdW5pRUNBNQd1bmlFQ0E3B3VuaUVDQTkHdW5pRUNCNwAAAAAAAAH//wACAAEA\ +AAAAAAAADAAUAAQAAAACAAAAAQAAAAEAAAAAAAEAAAAA399K6gAAAADRlyIXAAAAAOBezeg=\ +") format("truetype")' +var font_scale_tb={serif:1,serifBold:1,'sans-serif':1,'sans-serifBold':1,Palatino:1.1,monospace:1},txt_ff="text,serif",fmt_lock={} +var cfmt={"abc-version":"1",annotationfont:{name:"text,sans-serif",size:12},aligncomposer:1,beamslope:.4,bardef:{"[":"","[]":"","|:":"[|:","|::":"[|::","|:::":"[|:::",":|":":|]","::|":"::|]",":::|":":::|]","::":":][:"},breaklimit:.7,breakoneoln:true,cancelkey:true,composerfont:{name:txt_ff,style:"italic",size:14},composerspace:6,decoerr:true,dynalign:true,footerfont:{name:txt_ff,size:16},fullsvg:'',gchordfont:{name:"text,sans-serif",size:12},gracespace:new Float32Array([6,8,11]),graceslurs:true,headerfont:{name:txt_ff,size:16},historyfont:{name:txt_ff,size:16},hyphencont:true,indent:0,infofont:{name:txt_ff,style:"italic",size:14},infoname:'R "Rhythm: "\n\ +B "Book: "\n\ +S "Source: "\n\ +D "Discography: "\n\ +N "Notes: "\n\ +Z "Transcription: "\n\ +H "History: "',infospace:0,keywarn:true,leftmargin:1.4*CM,lineskipfac:1.1,linewarn:true,maxshrink:.65,maxstaffsep:2000,maxsysstaffsep:2000,measrepnb:1,measurefont:{name:txt_ff,style:"italic",size:10},measurenb:-1,musicfont:{name:"music",src:musicfont,size:24},musicspace:6,partsfont:{name:txt_ff,size:15},parskipfac:.4,partsspace:8,pagewidth:21*CM,"propagate-accidentals":"o",printmargin:0,rightmargin:1.4*CM,rbmax:4,rbmin:2,repeatfont:{name:txt_ff,size:9},scale:1,slurheight:1.0,spatab:new Float32Array([10.2,13.3,17.3,22.48,29.2,38,49.4,64.2,83.5,108.5]),staffsep:46,stemheight:21,stretchlast:.25,stretchstaff:true,subtitlefont:{name:txt_ff,size:16},subtitlespace:3,sysstaffsep:34,systnames:-1,tempofont:{name:txt_ff,weight:"bold",size:12},textfont:{name:txt_ff,size:16},textspace:14,tieheight:1.0,titlefont:{name:txt_ff,size:20},titlespace:6,titletrim:true,topspace:22,tuplets:[0,0,0,0],tupletfont:{name:txt_ff,style:"italic",size:10},vocalfont:{name:txt_ff,weight:"bold",size:13},vocalspace:10,voicefont:{name:txt_ff,weight:"bold",size:13},writefields:"CMOPQsTWw",wordsfont:{name:txt_ff,size:16},wordsspace:5,"writeout-accidentals":"n"} +var sfmt={bardef:true,barsperstaff:true,beamslope:true,breaklimit:true,bstemdown:true,cancelkey:true,dynalign:true,flatbeams:true,gracespace:true,hyphencont:true,keywarn:true,maxshrink:true,maxstaffsep:true,measrepnb:true,rbmax:true,rbmin:true,shiftunison:true,slurheight:true,squarebreve:true,staffsep:true,systnames:1,stemheight:true,stretchlast:true,stretchstaff:true,tieheight:true,timewarn:true,trimsvg:1,vocalspace:true} +function get_bool(param){return!param||!/^(0|n|f)/i.test(param)} +function get_font_scale(param){var i,font,a=info_split(param) +if(a.length<=1) +return +var scale=+a[a.length-1] +if(isNaN(scale)||scale<=0.5){syntax(1,"Bad scale value in %%font") +return} +font_scale_tb[a[0]]=scale} +function set_font_fac(font){var scale=font_scale_tb[font.fname||font.name] +if(!scale) +scale=1.1;font.swfac=font.size*scale} +function param_set_font(xxxfont,p){var font,n,a,ft2,k +if(xxxfont[xxxfont.length-2]=='-'){n=xxxfont[xxxfont.length-1] +if(n<'1'||n>'9') +return +xxxfont="u"+n+"font"} +font={} +a=p.match(/\s+(no)?box(\s|$)/) +if(a){if(a[1]){font.box=false +font.pad=0}else{font.box=true +font.pad=2.5} +p=p.replace(a[0],a[2])} +a=p.match(/\s+padding=([\d.]+)(\s|$)/) +if(a){font.pad=a[1]?+a[1]:0 +p=p.replace(a[0],a[2])} +a=p.match(/\s+class=(.*?)(\s|$)/) +if(a){font.class=a[1];p=p.replace(a[0],a[2])} +a=p.match(/\s+wadj=(.*?)(\s|$)/) +if(a){if(typeof document=="undefined") +switch(a[1]){case'none':font.wadj='' +break +case'space':font.wadj='spacing' +break +case'glyph':font.wadj='spacingAndGlyphs' +break +default:syntax(1,errs.bad_val,"%%"+xxxfont) +break} +p=p.replace(a[0],a[2])} +a=p.match(/\s+([0-9.]+|\*)$/) +if(a){if(a[1]!="*") +font.size=+a[1] +p=p.replace(a[0],"")} +if((p[0]=='u'&&p.slice(0,4)=="url(")||(p[0]=='l'&&p.slice(0,6)=="local(")){n=p.indexOf(')',1) +if(n<0){syntax(1,"No end of url in font family") +return} +font.src=p.slice(0,n+1) +font.fid=abc2svg.font_tb.length +abc2svg.font_tb.push(font) +font.name='ft'+font.fid +p=p.replace(font.src,'')} +a=p.match(/[- ]?[nN]ormal/) +if(a){font.normal=true +p=p.replace(a[0],'')} +a=p.match(abc2svg.ft_re) +if(a){font.weight=abc2svg.ft_w[a[0].replace(/[ -]/,'').toLowerCase()] +p=p.replace(a[0],'')} +a=p.match(/[- ]?[iI]talic/) +if(a){font.style="italic" +p=p.replace(a[0],'')} +a=p.match(/[- ]?[oO]blique/) +if(a){font.style="oblique" +p=p.replace(a[0],'')} +if(!font.src){if(p[0]=='"'){n=p.indexOf('"',1) +if(n<0){syntax(1,"No end of string in font family") +return} +p=p.slice(1,n)} +p=p.trim() +switch(p){case"":case"*":p="";break +case"Times-Roman":case"Times":p="serif";break +case"Helvetica":p="sans-serif";break +case"Courier":p="monospace";break +case"music":p=cfmt.musicfont.name;break +default:if(p.indexOf("Fig")>0) +font.figb=true +break}} +if(p&&!font.name) +font.name=p +if(font.size) +set_font_fac(font) +if(!font.name||!font.size){ft2=cfmt[xxxfont] +for(k in ft2){if(!ft2.hasOwnProperty(k)||font[k]!=undefined) +continue +switch(k){case"fid":case"used":case"src":break +case"style":case"weight":if(font.normal) +break +default:font[k]=ft2[k] +break}} +if(!font.swfac) +set_font_fac(font)} +if(font.pad==undefined) +font.pad=0 +font.fname=font.name +if(font.weight>=700) +font.fname+='Bold' +cfmt[xxxfont]=font} +function get_unit(param){var v=param.toLowerCase().match(/(-?[\d.]+)(.*)/) +if(!v) +return NaN +v[1]=+v[1] +switch(v[2]){case"cm":return v[1]*CM +case"in":return v[1]*IN +case"pt":return v[1]/.75 +case"px":case"":return v[1]} +return NaN} +function set_infoname(param){var tmp=cfmt.infoname.split("\n"),letter=param[0] +for(var i=0;i0) +v=v.slice(0,i) +return textopt[v]} +var posval={above:C.SL_ABOVE,auto:0,below:C.SL_BELOW,down:C.SL_BELOW,hidden:C.SL_HIDDEN,opposite:C.SL_HIDDEN,under:C.SL_BELOW,up:C.SL_ABOVE} +function set_pos(k,v){k=k.slice(0,3) +if(k=="ste") +k="stm" +set_v_param("pos",'"'+k+' '+v+'"')} +function set_writefields(parm){var c,i,a=parm.split(/\s+/) +if(get_bool(a[1])){for(i=0;i=0) +cfmt.writefields=cfmt.writefields.replace(c,'')}}} +function set_v_param(k,v){k=[k+'=',v] +if(parse.state<3) +memo_kv_parm(curvoice?curvoice.id:'*',k) +else if(curvoice) +set_kv_parm(k) +else +memo_kv_parm('*',k)} +function set_page(){if(!img.chg) +return +img.chg=false;img.lm=cfmt.leftmargin-cfmt.printmargin +if(img.lm<0) +img.lm=0;img.rm=cfmt.rightmargin-cfmt.printmargin +if(img.rm<0) +img.rm=0;img.width=cfmt.pagewidth-2*cfmt.printmargin +if(img.width-img.lm-img.rm<100){error(0,undefined,"Bad staff width");img.width=img.lm+img.rm+150} +set_posx()} +Abc.prototype.set_format=function(cmd,param){var f,f2,v,i +if(/.+font(-[\d])?$/.test(cmd)){if(cmd=="soundfont") +cfmt.soundfont=param +else +param_set_font(cmd,param) +return} +if(sfmt[cmd]&&parse.ufmt) +cfmt=Object.create(cfmt) +switch(cmd){case"aligncomposer":case"barsperstaff":case"infoline":case"measurenb":case"rbmax":case"rbmin":case"measrepnb":case"shiftunison":case"systnames":v=parseInt(param) +if(isNaN(v)){syntax(1,"Bad integer value");break} +cfmt[cmd]=v +break +case"abc-version":case"bgcolor":case"fgcolor":case"propagate-accidentals":case"titleformat":case"writeout-accidentals":cfmt[cmd]=param +break +case"beamslope":case"breaklimit":case"lineskipfac":case"maxshrink":case"pagescale":case"parskipfac":case"scale":case"slurheight":case"stemheight":case"tieheight":f=+param +if(isNaN(f)||!param||f<0){syntax(1,errs.bad_val,'%%'+cmd) +break} +switch(cmd){case"scale":f/=.75 +case"pagescale":if(f<.1) +f=.1 +cmd="scale";img.chg=true +break} +cfmt[cmd]=f +break +case"annotationbox":case"gchordbox":case"measurebox":case"partsbox":param_set_font(cmd.replace("box","font"),"* * "+(get_bool(param)?"box":"nobox")) +break +case"altchord":case"bstemdown":case"breakoneoln":case"cancelkey":case"checkbars":case"contbarnb":case"custos":case"decoerr":case"flatbeams":case"graceslurs":case"graceword":case"hyphencont":case"keywarn":case"linewarn":case"quiet":case"squarebreve":case"splittune":case"straightflags":case"stretchstaff":case"timewarn":case"titlecaps":case"titleleft":case"trimsvg":cfmt[cmd]=get_bool(param) +break +case"dblrepbar":param=":: "+param +case"bardef":v=param.split(/\s+/) +if(v.length!=2){syntax(1,errs.bad_val,"%%bardef")}else{if(parse.ufmt) +cfmt.bardef=Object.create(cfmt.bardef) +cfmt.bardef[v[0]]=v[1]} +break +case"chordalias":v=param.split(/\s+/) +if(!v.length) +syntax(1,errs.bad_val,"%%chordalias") +else +abc2svg.ch_alias[v[0]]=v[1]||"" +break +case"composerspace":case"indent":case"infospace":case"maxstaffsep":case"maxsysstaffsep":case"musicspace":case"partsspace":case"staffsep":case"subtitlespace":case"sysstaffsep":case"textspace":case"titlespace":case"topspace":case"vocalspace":case"wordsspace":f=get_unit(param) +if(isNaN(f)) +syntax(1,errs.bad_val,'%%'+cmd) +else +cfmt[cmd]=f +break +case"page-format":user.page_format=get_bool(param) +break +case"print-leftmargin":syntax(0,"$1 is deprecated - use %%printmargin instead",'%%'+cmd) +cmd="printmargin" +case"printmargin":case"leftmargin":case"pagewidth":case"rightmargin":f=get_unit(param) +if(isNaN(f)){syntax(1,errs.bad_val,'%%'+cmd) +break} +cfmt[cmd]=f;img.chg=true +break +case"concert-score":if(cfmt.sound!="play") +cfmt.sound="concert" +break +case"writefields":set_writefields(param) +break +case"volume":cmd="dynamic" +case"dynamic":case"gchord":case"gstemdir":case"ornament":case"stemdir":case"vocal":set_pos(cmd,param) +break +case"font":get_font_scale(param) +break +case"fullsvg":if(parse.state!=0){syntax(1,errs.not_in_tune,"%%fullsvg") +break} +cfmt[cmd]=param +break +case"gracespace":v=param.split(/\s+/) +for(i=0;i<3;i++) +if(isNaN(+v[i])){syntax(1,errs.bad_val,"%%gracespace") +break} +if(parse.ufmt) +cfmt[cmd]=new Float32Array(3) +for(i=0;i<3;i++) +cfmt[cmd][i]=+v[i] +break +case"tuplets":v=param.split(/\s+/) +f=v[3] +if(f) +f=posval[f] +if(f) +v[3]=f +if(curvoice) +curvoice.tup=v +else +cfmt[cmd]=v +break +case"infoname":set_infoname(param) +break +case"notespacingfactor":v=param.match(/([.\d]+)[,\s]*(\d+)?/) +if(v){f=+v[1] +if(isNaN(f)||f<1||f>2){f=0}else if(v[2]){f2=+v[2] +if(isNaN(f)) +f=0}else{f2=cfmt.spatab[5]}} +if(!f){syntax(1,errs.bad_val,"%%"+cmd) +break} +cfmt[cmd]=param +cfmt.spatab=new Float32Array(10) +i=5;do{cfmt.spatab[i]=f2 +f2/=f}while(--i>=0) +i=5;f2=cfmt.spatab[i] +for(;++i1){syntax(1,errs.bad_val,'%%'+cmd) +break}} +cfmt[cmd]=v +break +case"combinevoices":syntax(1,"%%combinevoices is deprecated - use %%voicecombine instead") +break +case"voicemap":set_v_param("map",param) +break +case"voicescale":set_v_param("scale",param) +break +case"unsizedsvg":if(get_bool(param)) +user.imagesize="" +else +delete user.imagesize +break +case"rbdbstop":v=get_bool(param) +if(v&&cfmt["abc-version"]>="2.2") +cfmt["abc-version"]="1" +else if(!v&&cfmt["abc-version"]<"2.2") +cfmt["abc-version"]="2.2" +break +default:if(!parse.state) +cfmt[cmd]=param +break} +if(sfmt[cmd]&&parse.ufmt){parse.ufmt=false}} +function st_font(font){var n=font.name,r="" +if(font.weight) +r+=font.weight+" " +if(font.style) +r+=font.style+" " +if(n.indexOf('"')<0&&n.indexOf(' ')>0) +n='"'+n+'"' +return r+font.size.toFixed(1)+'px '+n} +function style_font(font){return'font:'+st_font(font)} +Abc.prototype.style_font=style_font +function font_class(font){var f='f'+font.fid+cfmt.fullsvg +if(font.class) +f+=' '+font.class +if(font.box) +f+=' '+'box' +return f} +function use_font(font){if(!font.used){font.used=true;if(font.fid==undefined){font.fid=abc2svg.font_tb.length +abc2svg.font_tb.push(font) +if(!font.swfac) +set_font_fac(font) +if(!font.pad) +font.pad=0 +font.cw_tb=!font.name?ssw_tb:font.name.indexOf("ans")>0?ssw_tb:font.name.indexOf("ono")>0?mw_tb:sw_tb} +add_fstyle(".f"+font.fid+ +(cfmt.fullsvg||"")+"{"+style_font(font)+"}") +if(font.src) +add_fstyle("@font-face{\n\ + font-family:"+font.name+";\n\ + src:"+font.src+"}") +if(font==cfmt.musicfont) +add_fstyle(".f"+font.fid ++(cfmt.fullsvg||"") ++' text,tspan{white-space:pre}')}} +function get_font(fn){var font,font2,fid,st +fn+="font" +font=cfmt[fn] +if(!font){syntax(1,"Unknown font $1",fn) +return gene.curfont} +if(!font.name||!font.size){font2=Object.create(gene.deffont) +if(font.name) +font2.name=font.name +if(font.normal){if(font2.weight) +font2.weight=null +if(font2.style) +font2.style=null}else{if(font.weight) +font2.weight=font.weight +if(font.style) +font2.style=font.style} +if(font.src) +font2.src=font.src +if(font.size) +font2.size=font.size +st=st_font(font2) +if(font.class){font2.class=font.class +st+=' '+font.class} +fid=abc2svg.font_st[st] +if(fid!=undefined) +return abc2svg.font_tb[fid] +abc2svg.font_st[st]=abc2svg.font_tb.length +font2.fid=font2.used=undefined +font=font2} +use_font(font) +return font} +var sav={},mac={},maci={},modone={} +var abc_utf={"=D":"Đ","=H":"Ħ","=T":"Ŧ","=d":"đ","=h":"ħ","=t":"ŧ","/O":"Ø","/o":"ø","/L":"Ł","/l":"ł","vL":"Ľ","vl":"ľ","vd":"ď",".i":"ı","AA":"Å","aa":"å","AE":"Æ","ae":"æ","DH":"Ð","dh":"ð","OE":"Œ","oe":"œ","ss":"ß","TH":"Þ","th":"þ"} +var oct_acc={"1":"\u266f","2":"\u266d","3":"\u266e","4":"𝄪","5":"𝄫"} +function cnv_escape(src,flag){var c,c2,dst="",i,j=0 +while(1){i=src.indexOf('\\',j) +if(i<0) +break +dst+=src.slice(j,i);c=src[++i] +if(!c) +return dst+'\\' +switch(c){case'0':case'2':if(src[i+1]!='0') +break +c2=oct_acc[src[i+2]] +if(c2){dst+=c2;j=i+3 +continue} +break +case'u':j=Number("0x"+src.slice(i+1,i+5));if(isNaN(j)||j<0x20){dst+=src[++i]+"\u0306" +j=i+1 +continue} +c=String.fromCharCode(j) +if(c=='\\'){i+=4 +break} +dst+=c +j=i+5 +continue +case't':dst+='\t';j=i+1 +continue +case'n':dst+='\n';j=i+1 +continue +default:c2=abc_utf[src.slice(i,i+2)] +if(c2){dst+=c2;j=i+2 +continue} +c2=src[i+1] +if(!c2) +break +if(!/[A-Za-z]/.test(c2)) +break +switch(c){case'`':dst+=c2+"\u0300" +j=i+2 +continue +case"'":dst+=c2+"\u0301" +j=i+2 +continue +case'^':dst+=c2+"\u0302" +j=i+2 +continue +case'~':dst+=c2+"\u0303" +j=i+2 +continue +case'=':dst+=c2+"\u0304" +j=i+2 +continue +case'_':dst+=c2+"\u0305" +j=i+2 +continue +case'.':dst+=c2+"\u0307" +j=i+2 +continue +case'"':dst+=c2+"\u0308" +j=i+2 +continue +case'o':dst+=c2+"\u030a" +j=i+2 +continue +case'H':dst+=c2+"\u030b" +j=i+2 +continue +case'v':dst+=c2+"\u030c" +j=i+2 +continue +case'c':dst+=c2+"\u0327" +j=i+2 +continue +case';':dst+=c2+"\u0328" +j=i+2 +continue} +break} +if(flag=='w') +dst+='\\' +dst+=c +j=i+1} +return dst+src.slice(j)} +var include=0 +function do_include(fn){var file,parse_sav +if(!user.read_file){syntax(1,"No read_file support") +return} +if(include>2){syntax(1,"Too many include levels") +return} +file=user.read_file(fn) +if(!file){syntax(1,"Cannot read file '$1'",fn) +return} +include++;parse_sav=clone(parse);tosvg(fn,file);parse_sav.state=parse.state;parse=parse_sav;include--} +function tosvg(in_fname,file,bol,eof){var i,c,eol,end,select,line0,line1,last_info,opt,text,a,b,s,pscom,txt_add='\n' +function tune_selected(){var re,res,i=file.indexOf('K:',bol) +if(i<0){return false} +i=file.indexOf('\n',i) +if(parse.select.test(file.slice(parse.bol,i))) +return true +re=/\n\w*\n/;re.lastIndex=i;res=re.exec(file) +if(res) +eol=re.lastIndex +else +eol=eof +return false} +function uncomment(src,flag){if(!src) +return src +var i=src.indexOf('%') +if(i==0) +return'' +if(i>0) +src=src.replace(/([^\\])%.*/,'$1').replace(/\\%/g,'%');src=src.replace(/\s+$/,'') +if(flag&&src.indexOf('\\')>=0) +return cnv_escape(src,flag) +return src} +function end_tune(){generate() +cfmt=sav.cfmt;info=sav.info;char_tb=sav.char_tb;glovar=sav.glovar;maps=sav.maps;mac=sav.mac;maci=sav.maci;parse.tune_v_opts=null;parse.scores=null;parse.ufmt=false +delete parse.ctrl +init_tune() +img.chg=true;set_page()} +function do_voice(select,in_tune){var opt,bol +if(select=="end") +return +if(in_tune){if(!parse.tune_v_opts) +parse.tune_v_opts={};opt=parse.tune_v_opts}else{if(!parse.voice_opts) +parse.voice_opts={};opt=parse.voice_opts} +opt[select]=[] +while(1){bol=++eol +if(file[bol]!='%') +break +eol=file.indexOf('\n',eol);if(file[bol+1]!=line1) +continue +bol+=2 +if(eol<0) +text=file.slice(bol) +else +text=file.slice(bol,eol);a=text.match(/\S+/) +switch(a[0]){default:opt[select].push(uncomment(text,true)) +continue +case"score":case"staves":case"tune":case"voice":bol-=2 +break} +break} +eol=parse.eol=bol-1} +function tune_filter(){var o,opts,j,pc,h,i=file.indexOf('K:',bol) +i=file.indexOf('\n',i);h=file.slice(parse.bol,i) +for(i in parse.tune_opts){if(!parse.tune_opts.hasOwnProperty(i)) +continue +if(!(new RegExp(i)).test(h)) +continue +opts=parse.tune_opts[i] +for(j=0;jeof) +eol=eof;parse.eol=eol +while(1){eol-- +switch(file[eol]){case' ':case'\t':continue} +break} +eol++ +if(eol==bol){if(parse.state==1){parse.istart=bol;syntax(1,"Empty line in tune header - ignored")}else if(parse.state>=2){end_tune() +parse.state=0 +if(parse.select){eol=file.indexOf('\nX:',parse.eol) +if(eol<0) +eol=eof +parse.eol=eol}} +continue} +parse.istart=parse.bol=bol;parse.iend=eol;parse.line.index=0;line0=file[bol];line1=file[bol+1] +if((line0=='I'&&line1==':')||line0=='%'){if(line0=='%'&&parse.prefix.indexOf(line1)<0) +continue +if(file[bol+2]=='a'&&file[bol+3]=='b'&&file[bol+4]=='c'&&file[bol+5]==' '){bol+=6;line0=file[bol];line1=file[bol+1]}else{pscom=true}} +if(pscom){pscom=false;bol+=2 +text=file.slice(bol,eol) +a=text.match(/([^\s]+)\s*(.*)/) +if(!a||a[1][0]=='%') +continue +switch(a[1]){case"abcm2ps":case"ss-pref":parse.prefix=a[2] +continue +case"abc-include":do_include(uncomment(a[2])) +continue} +if(a[1].slice(0,5)=='begin'){b=a[1].substr(5);end='\n'+line0+line1+"end"+b;i=file.indexOf(end,eol) +if(i<0){syntax(1,"No $1 after %%$2",end.slice(1),a[1]);parse.eol=eof +continue} +self.do_begin_end(b,uncomment(a[2]),file.slice(eol+1,i).replace(/\n%[^%].*$/gm,'').replace(/^%%/gm,'')) +parse.eol=file.indexOf('\n',i+6) +if(parse.eol<0) +parse.eol=eof +continue} +switch(a[1]){case"select":if(parse.state!=0){syntax(1,errs.not_in_tune,"%%select") +continue} +select=uncomment(text.slice(7)) +if(select[0]=='"') +select=select.slice(1,-1);if(!select){delete parse.select +continue} +select=select.replace(/\(/g,'\\(');select=select.replace(/\)/g,'\\)');parse.select=new RegExp(select,'m') +continue +case"tune":if(parse.state!=0){syntax(1,errs.not_in_tune,"%%tune") +continue} +select=uncomment(a[2]) +if(!select){parse.tune_opts={} +continue} +if(select=="end") +continue +if(!parse.tune_opts) +parse.tune_opts={};parse.tune_opts[select]=opt={t_opts:[]};while(1){bol=eol +if(file[bol+1]!='%') +break +eol=file.indexOf('\n',eol+1) +if(file[bol+2]!=line1) +continue +text=file.slice(bol+3,eol<0?undefined:eol) +a=text.match(/([^\s]+)\s*(.*)/) +switch(a[1]){case"tune":break +case"voice":do_voice(uncomment(a[2],true),true) +continue +default:opt.t_opts.push(uncomment(text,true)) +continue} +break} +if(parse.tune_v_opts){opt.v_opts=parse.tune_v_opts;parse.tune_v_opts=null} +parse.eol=bol +continue +case"voice":if(parse.state!=0){syntax(1,errs.not_in_tune,"%%voice") +continue} +select=uncomment(a[2]) +if(!select){parse.voice_opts=null +continue} +do_voice(select) +continue} +self.do_pscom(uncomment(text,true)) +continue} +if(line1!=':'||!/[A-Za-z+]/.test(line0)){last_info=undefined;if(parse.state<2) +continue +parse.line.buffer=uncomment(file.slice(bol,eol)) +if(parse.line.buffer) +parse_music_line() +continue} +bol+=2 +while(1){switch(file[bol]){case' ':case'\t':bol++ +continue} +break} +if(line0=='+'){if(!last_info){syntax(1,"+: without previous info field") +continue} +txt_add=' ';line0=last_info} +text=uncomment(file.slice(bol,eol),line0) +switch(line0){case'X':if(parse.state!=0){syntax(1,errs.ignored,line0) +continue} +if(parse.select&&!tune_selected()){eol=file.indexOf('\nX:',parse.eol) +if(eol<0) +eol=eof;parse.eol=eol +continue} +sav.cfmt=clone(cfmt);sav.info=clone(info,2) +sav.char_tb=clone(char_tb);sav.glovar=clone(glovar);sav.maps=clone(maps,1);sav.mac=clone(mac);sav.maci=clone(maci);info.X=text;parse.state=1 +if(user.page_format&&blkdiv<1) +blkdiv=1 +if(parse.tune_opts) +tune_filter() +continue +case'T':switch(parse.state){case 0:continue +case 1:case 2:if(info.T==undefined) +info.T=text +else +info.T+="\n"+text +continue} +s=new_block("title");s.text=text +continue +case'K':switch(parse.state){case 0:continue +case 1:info.K=text +break} +do_info(line0,text) +continue +case'W':if(parse.state==0||cfmt.writefields.indexOf(line0)<0) +break +if(info.W==undefined) +info.W=text +else +info.W+=txt_add+text +break +case'm':if(parse.state>=2){syntax(1,errs.ignored,line0) +continue} +a=text.match(/(.*?)[= ]+(.*)/) +if(!a||!a[2]){syntax(1,errs.bad_val,"m:") +continue} +mac[a[1]]=a[2];maci[a[1][0]]=true +break +case's':if(parse.state!=3||cfmt.writefields.indexOf(line0)<0) +break +get_sym(text,txt_add==' ') +break +case'w':if(parse.state!=3||cfmt.writefields.indexOf(line0)<0) +break +get_lyrics(text,txt_add==' ') +break +case'|':if(parse.state<2) +continue +parse.line.buffer=text +parse_music_line() +continue +default:if("ABCDFGHNOSZ".indexOf(line0)>=0){if(parse.state>=2){syntax(1,errs.ignored,line0) +continue} +if(!info[line0]) +info[line0]=text +else +info[line0]+=txt_add+text +break} +do_info(line0,text) +continue} +txt_add='\n';last_info=line0} +if(include) +return +if(parse.state==1){syntax(1,"End of file in tune header") +get_key("C")} +if(parse.state>=2) +end_tune();parse.state=0} +Abc.prototype.tosvg=tosvg +var gene,staff_tb,nstaff,tsnext,realwidth,insert_meter,spf_last,smallest_duration +var dx_tb=new Float32Array([10,10,11,13,15]) +var hw_tb=new Float32Array([4.7,5,6,7.2,7.5]) +var w_note=new Float32Array([3.5,3.7,5,6,7]) +function identify_note(s,dur_o){var head,flags,dots=0,dur=dur_o +if(dur%12!=0) +error(1,s,"Invalid note duration $1",dur);dur/=12 +if(!dur) +error(1,s,"Note too short") +for(flags=5;dur;dur>>=1,flags--){if(dur&1) +break} +dur>>=1 +if((dur+1)&dur){if(s.type!=C.REST||dur_o!=s.p_v.wmeasure) +error(0,s,"Non standard note duration $1",dur_o)} +while(dur>>dots>0) +dots++ +flags-=dots +if(flags>=0){head=C.FULL}else switch(flags){default:error(1,s,"Note too long") +flags=-4 +case-4:head=C.SQUARE +break +case-3:head=s.fmt.squarebreve?C.SQUARE:C.OVALBARS +break +case-2:head=C.OVAL +break +case-1:head=C.EMPTY +break} +return[head,dots,flags]} +function set_head_shift(s){var i,i1,i2,d,ps,dx,dx_head=dx_tb[s.head],dir=s.stem,n=s.nhd +if(!n) +return +dx=dx_head*.74 +if(s.grace) +dx*=.6 +if(dir>=0){i1=1;i2=n+1;ps=s.notes[0].pit}else{dx=-dx;i1=n-1;i2=-1;ps=s.notes[n].pit} +var shift=false,dx_max=0 +for(i=i1;i!=i2;i+=dir){d=s.notes[i].pit-ps;ps=s.notes[i].pit +if(!d){if(shift){var new_dx=s.notes[i].shhd=s.notes[i-dir].shhd+dx +if(dx_max3||(d>=2&&s.head!=C.SQUARE)){shift=false}else{shift=!shift +if(shift){s.notes[i].shhd=dx +if(dx_max=0;){dx=notes[i].shhd +if(!dx||dx>0) +continue +dx=dx_head-dx;ps=notes[i].pit +for(i1=n;--i1>=0;){if(!notes[i1].acc) +continue +p1=notes[i1].pit +if(p1ps+3) +continue +if(notes[i1].shac=0;){if(notes[i1].acc){p1=notes[i1].pit +dx1=notes[i1].shac +if(!dx1){dx1=notes[i1].shhd +if(dx1<0) +dx1=dx_head-dx1 +else +dx1=dx_head} +break}} +if(i1<0) +return +for(i2=0;i2ps+4){if(dx1>dx2) +dx2=dx1 +notes[i1].shac=notes[i2].shac=dx2}else{notes[i1].shac=dx1 +if(notes[i1].pit!=notes[i2].pit) +dx1+=7 +notes[i2].shac=dx2=dx1} +dx2+=7 +for(i=i1;--i>i2;){acc=notes[i].acc +if(!acc) +continue +dx=notes[i].shac +if(dxi;){if(!notes[i1].acc) +continue +p1=notes[i1].pit +if(p1>=ps+4){if(p1>ps+4||acc<0||notes[i1].acc<0) +continue} +if(dx>notes[i1].shac-6){dx1=notes[i1].shac+7 +if(dx1>dx) +dx=dx1}} +notes[i].shac=dx}} +function set_acc_shft(){var s,s2,st,i,acc,st,t,dx_head,notes +s=tsfirst +while(s){if(s.type!=C.NOTE||s.invis){s=s.ts_next +continue} +st=s.st;t=s.time;acc=false +for(s2=s;s2;s2=s2.ts_next){if(s2.time!=t||s2.type!=C.NOTE||s2.st!=st) +break +if(acc) +continue +for(i=0;i<=s2.nhd;i++){if(s2.notes[i].acc){acc=true +break}}} +if(!acc){s=s2 +continue} +dx_head=dx_tb[s.head] +notes=[] +for(;s!=s2;s=s.ts_next){if(!s.invis) +Array.prototype.push.apply(notes,s.notes)} +notes.sort(abc2svg.pitcmp) +acc_shift(notes,dx_head)}} +function lkvsym(s,next){s.next=next;s.prev=next.prev +if(s.prev) +s.prev.next=s +else +s.p_v.sym=s;next.prev=s} +function lktsym(s,next){var old_wl +s.ts_next=next +if(next){s.ts_prev=next.ts_prev +if(s.ts_prev) +s.ts_prev.ts_next=s;next.ts_prev=s}else{error(2,s,"Bad linkage") +s.ts_prev=null} +s.seqst=!s.ts_prev||s.time!=s.ts_prev.time||(w_tb[s.ts_prev.type]!=w_tb[s.type]&&!!w_tb[s.ts_prev.type]) +if(!next||next.seqst) +return +next.seqst=next.time!=s.time||(w_tb[s.type]!=w_tb[next.type]&&!!w_tb[s.type]) +if(next.seqst){old_wl=next.wl +self.set_width(next) +if(next.a_ly) +ly_set(next) +if(!next.shrink){next.shrink=next.wl +if(next.prev) +next.shrink+=next.prev.wr}else{next.shrink+=next.wl-old_wl} +next.space=0}} +function unlksym(s){if(s.next) +s.next.prev=s.prev +if(s.prev) +s.prev.next=s.next +else +s.p_v.sym=s.next +if(s.ts_next){if(s.seqst){if(s.ts_next.seqst){s.ts_next.shrink+=s.shrink;s.ts_next.space+=s.space}else{s.ts_next.seqst=true;s.ts_next.shrink=s.shrink;s.ts_next.space=s.space}}else{if(s.ts_next.seqst&&s.ts_prev&&s.ts_prev.seqst&&!w_tb[s.ts_prev.type]){s.ts_next.seqst=false +s.shrink=s.ts_next.shrink +s.space=s.ts_next.space}} +s.ts_next.ts_prev=s.ts_prev} +if(s.ts_prev) +s.ts_prev.ts_next=s.ts_next +if(tsfirst==s) +tsfirst=s.ts_next +if(tsnext==s) +tsnext=s.ts_next} +function insert_clef(s,clef_type,clef_line){var p_voice=s.p_v,new_s,st=s.st +if(s.type==C.BAR&&s.prev&&s.prev.type==C.BAR&&s.prev.bar_type[0]!=':') +s=s.prev;p_voice.last_sym=s.prev +if(!p_voice.last_sym) +p_voice.sym=null;p_voice.time=s.time;new_s=sym_add(p_voice,C.CLEF);new_s.next=s;s.prev=new_s;new_s.clef_type=clef_type;new_s.clef_line=clef_line;new_s.st=st;new_s.clef_small=true +delete new_s.second;new_s.notes=[] +new_s.notes[0]={pit:s.notes[0].pit} +new_s.nhd=0;while(!s.seqst) +s=s.ts_prev;lktsym(new_s,s) +if(s.soln){new_s.soln=true +delete s.soln} +return new_s} +function set_float(){var p_voice,st,staff_chg,v,s,s1,up,down +for(v=0;v=19){staff_chg=false +continue} +if(s.notes[s.nhd].pit<=12){staff_chg=true +s.st++ +continue} +up=127 +for(s1=s.ts_prev;s1;s1=s1.ts_prev){if(s1.st!=st||s1.v==s.v) +break +if(s1.type==C.NOTE) +if(s1.notes[0].pitup-3){staff_chg=false +continue} +down=-127 +for(s1=s.ts_next;s1;s1=s1.ts_next){if(s1.st!=st+1||s1.v==s.v) +break +if(s1.type==C.NOTE) +if(s1.notes[s1.nhd].pit>down) +down=s1.notes[s1.nhd].pit} +if(down==-127){if(staff_chg) +s.st++ +continue} +if(s.notes[0].pit=0;m--){if(g.notes[m].shac-2>dx) +dx=g.notes[m].shac-2} +x+=dx;g.x=x +if(g.nflags<=0) +g.beam_st=g.beam_end=true +next=g.next +if(!next){g.beam_end=true +break} +if(next.nflags<=0) +g.beam_end=true +if(g.beam_end){next.beam_st=true;x+=gspinside/4} +if(g.nflags<=0) +x+=gspinside/4 +if(g.y>next.y+8) +x-=1.5 +x+=gspinside} +next=s.next +if(next&&next.type==C.NOTE){if(g.y>=3*(next.notes[next.nhd].pit-18)) +gspright-=1 +else if(g.beam_st&&g.y<3*(next.notes[next.nhd].pit-18)-4) +gspright+=2} +x+=gspright;return x} +function set_w_chs(s){var i,ch,w0,s0,dw,x=0,n=0 +set_font("vocal") +for(;s;s=s.ts_next){if(s.shrink){x+=s.shrink;n++} +if(s.a_ly) +ly_set(s) +if(!s.a_gch) +continue +for(i=0;ix+ch.x){if(s.prev&&s.prev.seqst&&s.prev.type==C.BAR) +n-- +dw=(w0-x-ch.x)/n +while(1){s0=s0.ts_next +if(s0.shrink) +s0.shrink+=dw +if(s0==s||s0.type==C.BAR) +break}}} +s0=s;w0=ch.text.wh[0];n=0;x=0 +break}}} +function gchord_width(s,wlnote,wlw){var gch,w,ix,arspc=0 +for(ix=0;ixwlw) +wlw=w +break +case'>':w=gch.text.wh[0]+s.wr +if(w>arspc) +arspc=w +break}} +if(s.wr0) +s.wr+=s.xmx+4;for(s2=s.prev;s2;s2=s2.prev){if(w_tb[s2.type]) +break} +if(s2){switch(s2.type){case C.BAR:case C.CLEF:case C.KEY:case C.METER:wlnote+=3 +break +case C.STBRK:wlnote+=8 +break}} +for(m=0;m<=s.nhd;m++){nt=s.notes[m] +xx=nt.shhd +if(xx<0){if(wlnote<-xx+5) +wlnote=-xx+5} +acc=nt.acc +if(acc){tmp=nt.shac+ +(typeof acc=="object"?5.5:3.5) +if(wlnote0&&s.nflags>0){if(s.wr=2) +s.wr+=3.5*(s.dots-1)} +if(s.trem2&&s.beam_end&&wlnote<20) +wlnote=20 +wlw=wlnote +if(s2){switch(s2.type){case C.NOTE:if(s2.stem>0&&s.stem<0){if(wlw<7) +wlw=7} +if((s.y>27&&s2.y>27)||(s.y<-3&&s2.y<-3)){if(wlw<6) +wlw=6} +if(s2.tie){if(wlw<14) +wlw=14} +break +case C.CLEF:if(s2.second||s2.clef_small) +break +case C.KEY:if(s.a_gch) +wlw+=4 +case C.METER:wlw+=3 +break}} +if(s.a_gch) +wlw=gchord_width(s,wlnote,wlw) +if(s.prev&&s.prev.type==C.GRACE){s.prev.wl+=wlnote-4.5 +s.wl=s.prev.wl}else{s.wl=wlw} +return +case C.SPACE:xx=s.width/2;s.wr=xx +if(s.a_gch) +xx=gchord_width(s,xx,xx) +if(s.a_dd) +xx=deco_width(s,xx) +s.wl=xx +return +case C.BAR:bar_type=s.bar_type +switch(bar_type){case"|":w=5 +break +case"[":w=0 +break +default:w=2+2.8*bar_type.length +for(i=0;i0&s.bar_num&&s.bar_num%cfmt.measurenb) +s.wr+=4} +return +case C.CLEF:if(s.invis){s.wl=s.wr=1 +return} +if(s.prev&&s.prev.type==C.STBRK){s.wl=6 +s.wr=13 +delete s.clef_small +return} +s.wl=s.clef_small?11:12 +s.wr=s.clef_small?8:13 +return +case C.KEY:if(s.invis){s.wl=s.wr=0 +return} +s.wl=0 +esp=3 +n1=s.k_sf +if(s.k_old_sf&&(s.fmt.cancelkey||n1==0)) +n2=s.k_old_sf +else +n2=0 +if(n1*n2>=0){if(n1<0) +n1=-n1 +if(n2<0) +n2=-n2 +if(n2>n1) +n1=n2}else{n1-=n2 +if(n1<0) +n1=-n1;esp+=3} +if(s.k_bagpipe=='p') +n1++ +if(s.k_a_acc){n2=s.k_a_acc.length +if(s.exp) +n1=n2 +else +n1+=n2 +if(n2) +last_acc=s.k_a_acc[0].acc +for(i=1;is.k_a_acc[i-1].pit+6||acc.pitmeter.bot.length) +meter=meter.top +else +meter=meter.bot;for(m=0;m=C.BLEN/2){if(smallest_duration>=C.BLEN) +len/=4 +else +len/=2}else if(!s.next&&len>=C.BLEN){len/=2} +if(len>=C.BLEN/4){if(len=C.BLEN/8) +i=4 +else if(len>=C.BLEN/16) +i=3 +else if(len>=C.BLEN/32) +i=2 +else if(len>=C.BLEN/64) +i=1 +else +i=0} +l=len-((C.BLEN/16/8)<=9) +i=8 +space+=(cfmt.spatab[i+1]-cfmt.spatab[i])*l/((C.BLEN/16/8)<=-1&&s.stem>0){stemdir=true +for(s2=s.ts_prev;s2&&s2.time==ptime;s2=s2.ts_prev){if(s2.type==C.NOTE&&(s2.nflags<-1||s2.stem>0)){stemdir=false +break}} +if(stemdir){for(s2=s.ts_next;s2&&s2.time==s.time;s2=s2.ts_next){if(s2.type==C.NOTE&&(s2.nflags<-1||s2.stem<0)){stemdir=false +break}} +if(stemdir) +space*=.9}} +return space} +function set_sp_tup(s,s_et){var tim=s.time,ttim=s_et.time-tim,sp=time2space(s,ttim),s2=s,wsp=0 +while(1){s2=s2.ts_next +if(s2.seqst){wsp+=s2.space +if(s2.bar_type) +wsp+=10} +if(s2==s_et) +break} +sp=(sp+wsp)/2/ttim +while(1){s=s.ts_next +if(s.seqst){s.space=sp*(s.time-tim) +tim=s.time} +if(s==s_et) +break}} +function _bar(s){return{type:C.BAR,bar_type:"|",fname:s.fname,istart:s.istart,iend:s.iend,v:s.v,p_v:s.p_v,st:s.st,dur:0,time:s.time+(s.dur||0),nhd:0,notes:[{pit:s.notes?s.notes[0].pit:22}],seqst:true,invis:true,prev:s,fmt:s.fmt}} +function add_end_bar(s){var b=_bar(s),sn=s.ts_next +b.wl=0 +b.wr=0 +b.ts_prev=s +b.next=s.next +b.ts_next=s.ts_next +b.shrink=s.type==C.STBRK?0:(s.wr+3) +if(s.next) +s.next.prev=b +s.ts_next.ts_prev=b +s.next=s.ts_next=b +b.space=sn.space*.9-3 +return b} +function set_allsymwidth(first){var val,st,s_chs,stup,itup,s=tsfirst,s2=s,xa=0,xl=[],wr=[],maxx=xa,tim=s.time +while(1){itup=0 +do{if((s.a_gch||s.a_ly)&&!s_chs) +s_chs=s;self.set_width(s);st=s.st +if(xl[st]==undefined) +xl[st]=0 +if(wr[st]==undefined) +wr[st]=0;val=xl[st]+wr[st]+s.wl +if(val>maxx) +maxx=val +if(s.dur&&s.dur!=s.notes[0].dur&&first) +itup=1 +s=s.ts_next}while(s&&!s.seqst);s2.shrink=maxx-xa +s2.space=s2.ts_prev?set_space(s2,tim):0 +if(s2.space==0&&s2.ts_prev&&s2.ts_prev.type==C.SPACE&&s2.ts_prev.seqst) +s2.space=s2.ts_prev.space/=2 +if(itup){if(!stup) +stup=s2}else if(stup&&stup.v==s2.v){set_sp_tup(stup,s2) +stup=null} +if(!s2.shrink){if(s2.type==C.CLEF&&!s2.ts_prev.bar_type){delete s2.seqst;s2.time=tim}else{s2.shrink=10}} +tim=s2.time +if(!s) +break +s=s2 +do{wr[s.st]=0 +s=s.ts_next}while(!s.seqst) +xa=maxx +do{st=s2.st;xl[st]=xa +if(s2.wr>wr[st]) +wr[st]=s2.wr +s2=s2.ts_next}while(!s2.seqst)} +if(stup) +set_sp_tup(stup,s2) +if(first&&s_chs) +set_w_chs(s_chs)} +function to_rest(so){var s=clone(so) +s.prev.next=so.ts_prev=so.prev=s.ts_prev.ts_next=s +s.next=s.ts_next=so +so.seqst=false +so.invis=so.play=true +s.type=C.REST +delete s.in_tuplet +delete s.tp +delete s.a_dd +delete s.a_gch +delete s.sls +return s} +function set_repeat(s){var s2,s3,i,j,dur,n=s.repeat_n,k=s.repeat_k,st=s.st,v=s.v +s.repeat_n=0 +if(n<0){n=-n;i=n +for(s3=s.prev;s3;s3=s3.prev){if(!s3.dur){if(s3.type==C.BAR){error(1,s3,"Bar in repeat sequence") +return} +continue} +if(--i<=0) +break} +if(!s3){error(1,s,errs.not_enough_n) +return} +dur=s.time-s3.time;i=k*n +for(s2=s;s2;s2=s2.next){if(!s2.dur){if(s2.type==C.BAR){error(1,s2,"Bar in repeat sequence") +return} +continue} +if(--i<=0) +break} +if(!s2||!s2.next){error(1,s,errs.not_enough_n) +return} +for(s2=s.prev;s2!=s3;s2=s2.prev){if(s2.type==C.NOTE){s2.beam_end=true +break}} +for(j=k;--j>=0;){i=n +if(s.dur) +i--;s2=s.ts_next +while(i>0){if(s2.st==st){s2.invis=s2.play=true +if(s2.seqst&&s2.ts_next.seqst) +s2.seqst=false +if(s2.v==v&&s2.dur) +i--} +s2=s2.ts_next} +s=to_rest(s) +s.dur=s.notes[0].dur=dur;s.rep_nb=-1;s.beam_st=true;self.set_width(s) +s.head=C.SQUARE;for(s=s2;s;s=s.ts_next){if(s.st==st&&s.v==v&&s.dur) +break}} +return} +i=n +for(s2=s.prev.prev;s2;s2=s2.prev){if(s2.type==C.BAR||s2.time==tsfirst.time){if(--i<=0) +break}} +if(!s2){error(1,s,errs.not_enough_m) +return} +dur=s.time-s2.time +if(n==1) +i=k +else +i=n +for(s2=s;s2;s2=s2.next){if(s2.type==C.BAR){if(--i<=0) +break}} +if(!s2){error(1,s,errs.not_enough_m) +return} +i=k +if(n==2&&i>1){s2=s2.next +if(!s2){error(1,s,errs.not_enough_m) +return} +s2.repeat_n=n;s2.repeat_k=--i} +dur/=n +if(n==2){s3=s +for(s2=s.ts_next;;s2=s2.ts_next){if(s2.st!=st) +continue +if(s2.type==C.BAR){if(s2.v==v) +break +continue} +s2.invis=s2.play=true +if(s2.seqst&&s2.ts_next.seqst) +s2.seqst=false} +s3=to_rest(s3) +s3.dur=s3.notes[0].dur=dur;s3.invis=true +s2.bar_mrep=2 +s3=s2.next;for(s2=s3.ts_next;;s2=s2.ts_next){if(s2.st!=st) +continue +if(s2.type==C.BAR){if(s2.v==v) +break +continue} +if(!s2.dur) +continue +s2.invis=s2.play=true +if(s2.seqst&&s2.ts_next.seqst) +s2.seqst=false} +s3=to_rest(s3) +s3.dur=s3.notes[0].dur=dur;s3.invis=true;self.set_width(s3) +return} +s3=s +for(j=k;--j>=0;){for(s2=s3.ts_next;;s2=s2.ts_next){if(s2.st!=st) +continue +if(s2.type==C.BAR){if(s2.v==v) +break +continue} +if(!s2.dur) +continue +s2.invis=s2.play=true +if(s2.seqst&&s2.ts_next.seqst) +s2.seqst=false} +s3=to_rest(s3) +s3.dur=s3.notes[0].dur=dur;s3.beam_st=true +if(k==1){s3.rep_nb=1 +break} +s3.rep_nb=k-j+1;s3=s2.next}} +function custos_add(s){var p_voice,new_s,i,s2=s +while(1){if(s2.type==C.NOTE) +break +s2=s2.next +if(!s2) +return} +p_voice=s.p_v;p_voice.last_sym=s.prev;p_voice.time=s.time;new_s=sym_add(p_voice,C.CUSTOS);new_s.next=s;s.prev=new_s;new_s.wl=0 +new_s.wr=4 +lktsym(new_s,s);new_s.shrink=s.shrink +if(new_s.shrink<8+4) +new_s.shrink=8+4;new_s.space=s2.space;new_s.head=C.FULL +new_s.stem=s2.stem +new_s.nhd=s2.nhd;new_s.notes=[] +for(i=0;iw) +w=s4.wr +if(s4.seqst) +break +s4=s4.ts_prev} +s3.shrink+=w +s3.space=0 +s4=s3 +while(1){if(s4.ts_next.seqst) +break +s4=s4.ts_next} +w=0 +while(1){if(s4.wl>w) +w=s4.wl +s4=s4.ts_next +if(s4.seqst) +break} +s4.shrink=s3.wr+w} +delete s3.part +continue} +if(w_tb[s2.type]) +break}} +s=bardiv(s) +do_warn(s) +if(s.ts_prev.type!=C.STAVES){set_eol(s) +return s} +for(s=s.ts_prev;s;s=s.ts_prev){if(s.seqst&&s.type!=C.CLEF) +break} +done=0 +ptyp=s.type +for(;;s=s.ts_next){if(!s) +return s +if(s.type==ptyp) +continue +ptyp=s.type +if(done<0) +break +switch(s.type){case C.STAVES:if(!s.ts_prev) +return +if(s.ts_prev.type==C.BAR) +break +while(s.ts_next){if(w_tb[s.ts_next.type]&&s.ts_next.type!=C.CLEF) +break +s=s.ts_next} +if(!s.ts_next||s.ts_next.type!=C.BAR) +continue +s=s.ts_next +case C.BAR:if(done) +break +done=1;continue +case C.STBRK:if(!s.stbrk_forced) +unlksym(s) +else +done=-1 +continue +case C.CLEF:if(done) +break +continue +default:if(!done||(s.prev&&s.prev.type==C.GRACE)) +continue +break} +break} +set_eol(s) +return s} +function get_ck_width(){var r0,r1,p_voice=voice_tb[0] +self.set_width(p_voice.clef);self.set_width(p_voice.ckey);self.set_width(p_voice.meter) +return[p_voice.clef.wl+p_voice.clef.wr+ +p_voice.ckey.wl+p_voice.ckey.wr,p_voice.meter.wl+p_voice.meter.wr]} +function get_width(s,next){var shrink,space,w=0,wmx=0,sp_fac=(1-s.fmt.maxshrink) +while(s!=next){if(s.seqst){shrink=s.shrink +wmx+=shrink +if((space=s.space)=xmin) +break} +s4=s +if(s==next){if(s) +s=set_nl(s) +return s} +s3=null +for(;s!=next;s=s.ts_next){x=s.x +if(!x) +continue +if(x>xmax) +break +if(s.type!=C.BAR) +continue +if(x=xmax) +break +if(!beam&&!s.in_tuplet&&(xmid-s5.x>x-xmid||(s.time-bar_time)%(C.BLEN/4)==0)) +s3=s} +if(s.beam_st) +beam|=1<=xmax) +break +if(s3&&x>=xmid){if(xmid-s3.x>x-xmid) +s3=s +break} +s3=s}} +s=s3 +while(s.ts_next){s=s.ts_next +if(s.seqst) +break} +if(s.nl){error(0,s,"Line split problem - adjust maxshrink and/or breaklimit");nlines=2 +for(s=s.ts_next;s!=next;s=s.ts_next){if(!s.x) +continue +if(--nlines<=0) +break}} +s=set_nl(s) +if(!s||(next&&s.time>=next.time)) +break +wwidth-=s.x-first.x;indent=0} +return s} +function cut_tune(lwidth,lsh){var s2,i,mc,pg_sav={leftmargin:cfmt.leftmargin,rightmargin:cfmt.rightmargin,pagewidth:cfmt.pagewidth,scale:cfmt.scale},indent=lsh[0]-lsh[1],ckw=get_ck_width(),s=tsfirst +lwidth-=lsh[1] +if(cfmt.indent&&cfmt.indent>lsh[0]) +indent+=cfmt.indent +lwidth-=ckw[0] +indent+=ckw[1] +if(cfmt.custos&&voice_tb.length==1) +lwidth-=12 +i=s.fmt.barsperstaff +if(i){for(s2=s;s2;s2=s2.ts_next){if(s2.type!=C.BAR||!s2.bar_num||--i>0) +continue +while(s2.ts_next&&s2.ts_next.type==C.BAR) +s2=s2.ts_next +if(s2.ts_next) +s2.ts_next.soln=true +i=s.fmt.barsperstaff}} +s2=s +for(;s;s=s.ts_next){if(s.type==C.BLOCK){switch(s.subtype){case"leftmargin":case"rightmargin":case"pagescale":case"pagewidth":case"scale":case"staffwidth":if(!s.soln) +self.set_format(s.subtype,s.param) +break +case"mc_start":mc={lm:cfmt.leftmargin,rm:cfmt.rightmargin} +break +case"mc_new":case"mc_end":if(!mc) +break +cfmt.leftmargin=mc.lm +cfmt.rightmargin=mc.rm +img.chg=1 +break}} +if(!s.ts_next){s=null}else if(!s.soln){continue}else{s.soln=false +if(s.time==s2.time) +continue +while(!s.seqst) +s=s.ts_prev} +set_page() +lwidth=get_lwidth()-lsh[1]-ckw[0] +s2=set_lines(s2,s,lwidth,indent) +if(!s2) +break +s=s2.type==C.BLOCK?s2.ts_prev:s +indent=0} +cfmt.leftmargin=pg_sav.leftmargin +cfmt.rightmargin=pg_sav.rightmargin +cfmt.pagewidth=pg_sav.pagewidth +cfmt.scale=pg_sav.scale +img.chg=1 +set_page()} +function set_yval(s){switch(s.type){case C.CLEF:if(s.second||s.invis){s.ymx=s.ymn=12 +break} +s.y=(s.clef_line-1)*6 +switch(s.clef_type){default:s.ymx=s.y+28 +s.ymn=s.y-14 +break +case"c":s.ymx=s.y+13 +s.ymn=s.y-11 +break +case"b":s.ymx=s.y+7 +s.ymn=s.y-12 +break} +if(s.clef_small){s.ymx-=2;s.ymn+=2} +if(s.ymx<26) +s.ymx=26 +if(s.ymn>-1) +s.ymn=-1 +if(s.clef_octave){if(s.clef_octave>0) +s.ymx+=12 +else +s.ymn-=12} +break +case C.KEY:if(s.k_sf>2) +s.ymx=24+10 +else if(s.k_sf>0) +s.ymx=24+6 +else +s.ymx=24+2;s.ymn=-2 +break +default:s.ymx=24;s.ymn=0 +break}} +function set_ottava(){var s,s1,st,o,d,m=nstaff+1,staff_d=new Int8Array(m) +function sym_ott(s,d){var g,m,note +switch(s.type){case C.REST:if(voice_tb.length==1) +break +case C.NOTE:if(!s.p_v.ckey.k_drum){for(m=s.nhd;m>=0;m--){note=s.notes[m];if(!note.opit) +note.opit=note.pit;note.pit+=d}} +break +case C.GRACE:for(g=s.extra;g;g=g.next){if(!s.p_v.ckey.k_drum){for(m=0;m<=g.nhd;m++){note=g.notes[m] +if(!note.opit) +note.opit=note.pit +note.pit+=d}}} +break}} +function deco_rm(s){for(var i=s.a_dd.length;--i>=0;){if(s.a_dd[i].name.match(/1?[85][vm][ab]/)) +s.a_dd.splice(i,1)}} +for(s=tsfirst;s;s=s.ts_next){st=s.st +o=s.ottava +if(o){if(o[0]){if(staff_d[st]&&!o[1]){sym_ott(s,staff_d[st]) +deco_rm(s) +continue}}else if(!staff_d[st]){deco_rm(s) +continue} +s1=s +while(s1&&!s1.seqst) +s1=s1.ts_prev +if(s1){while(s1!=s){if(s1.st==st){if(o[1]) +sym_ott(s1,-staff_d[st]) +if(o[0]) +sym_ott(s1,-o[0]*7)} +s1=s1.ts_next}} +if(o[0]){staff_d[st]=-o[0]*7}else{staff_d[st]=0}} +if(staff_d[st]) +sym_ott(s,staff_d[st])}} +function mrest_expand(){var s,s2 +function mexp(s){var bar,s3,s4,tim,nbar,nb=s.nmes,dur=s.dur/nb,s2=s.next +while(!s2.bar_type) +s2=s2.next +bar=s2 +while(!s2.bar_num) +s2=s2.ts_prev +nbar=s2.bar_num-s.nmes +s.type=C.REST +s.notes[0].dur=s.dur=s.dur_orig=dur +s.nflags=-2 +s.head=C.FULL +s.fmr=1 +tim=s.time+dur +s3=s +while(--nb>0){s2=clone(bar) +delete s2.soln +delete s2.a_gch +delete s2.a_dd +delete s2.text +delete s2.rbstart +delete s2.rbstop +lkvsym(s2,s.next) +s2.time=tim +while(s3.timemax) +max=s.notes[s.nhd].pit} +if(min>=19||(min>=13&&clef_type_start!='b')) +return't' +if(max<=13||(max<=19&&clef_type_start!='t')) +return'b' +if(clef_type_start=='a'){if((max+min)/2>=16) +clef_type_start='t' +else +clef_type_start='b'} +var clef_type=clef_type_start,s_last=s,s_last_chg=null +for(s=s_start;s!=s_last;s=s.ts_next){if(s.type==C.STAVES&&s!=s_start) +break +if(s.st!=st||s.type!=C.NOTE) +continue +time=s.time +if(clef_type=='t'){if(s.notes[0].pit>12||s.notes[s.nhd].pit>20){if(s.notes[0].pit>20) +s_last_chg=s +continue} +s2=s.ts_prev +if(s2&&s2.time==time&&s2.st==st&&s2.type==C.NOTE&&s2.notes[0].pit>=19) +continue +s2=s.ts_next +if(s2&&s2.st==st&&s2.time==time&&s2.type==C.NOTE&&s2.notes[0].pit>=19) +continue}else{if(s.notes[0].pit<=12||s.notes[s.nhd].pit<20){if(s.notes[s.nhd].pit<=12) +s_last_chg=s +continue} +s2=s.ts_prev +if(s2&&s2.time==time&&s2.st==st&&s2.type==C.NOTE&&s2.notes[0].pit<=13) +continue +s2=s.ts_next +if(s2&&s2.st==st&&s2.time==time&&s2.type==C.NOTE&&s2.notes[0].pit<=13) +continue} +if(!s_last_chg){clef_type=clef_type_start=clef_type=='t'?'b':'t';s_last_chg=s +continue} +s3=s +for(s2=s.ts_prev;s2!=s_last_chg;s2=s2.ts_prev){if(s2.st!=st) +continue +if(s2.type==C.BAR){s3=s2.bar_type[0]!=':'?s2:s2.next +break} +if(s2.type!=C.NOTE) +continue +if(s2.beam_st&&!s2.p_v.second) +s3=s2} +if(s3.time==s_last_chg.time){s_last_chg=s +continue} +s_last_chg=s;clef_type=clef_type=='t'?'b':'t';s2=insert_clef(s3,clef_type,clef_type=="t"?2:4);s2.clef_auto=true} +return clef_type_start} +function set_clefs(){var s,s2,st,v,p_voice,g,new_type,new_line,p_staff,pit,staff_clef=new Array(nstaff+1),sy=cur_sy,mid=[] +staff_tb=new Array(nstaff+1) +for(st=0;st<=nstaff;st++){staff_clef[st]={autoclef:true} +staff_tb[st]={output:"",sc_out:""}} +for(st=0;st<=sy.nstaff;st++) +mid[st]=(sy.staves[st].stafflines.length-1)*3 +for(s=tsfirst;s;s=s.ts_next){if(s.repeat_n) +set_repeat(s) +switch(s.type){case C.STAVES:sy=s.sy +for(st=0;st<=nstaff;st++) +staff_clef[st].autoclef=true +for(v=0;v1||s.bar_mrep){s.y=12 +s.ymx=38 +s.ymn=0 +break} +if(voice_tb.length==1){s.y=12;s.ymx=24;s.ymn=0 +break} +case C.NOTE:delta=staff_delta[st] +if(delta&&!s.p_v.ckey.k_drum){for(m=s.nhd;m>=0;m--){note=s.notes[m] +note.opit=note.pit +note.pit+=delta}} +if(s.type==C.REST){s.y=(((s.notes[0].pit-18)/2)|0)*6;s.ymx=s.y+rest_sp[5-s.nflags][0];s.ymn=s.y-rest_sp[5-s.nflags][1]} +if(s.durnst){var msg="*** fatal set_stem_dir(): bad staff number "+st+" max "+nst;error(2,null,msg);throw new Error(msg)} +v=u.v;v_st=v_st_tb[v] +if(!v_st){v_st={st1:-1,st2:-1} +v_st_tb[v]=v_st} +if(v_st.st1<0){v_st.st1=st}else if(v_st.st1!=st){if(st>v_st.st1){if(st>v_st.st2) +v_st.st2=st}else{if(v_st.st1>v_st.st2) +v_st.st2=v_st.st1;v_st.st1=st}} +st_v=st_v_tb[st];rvoice=sy.voices[v].range;for(i=st_v.length;--i>=0;){vobj=st_v[i] +if(vobj.v==rvoice) +break} +if(i<0){vobj={v:rvoice,ymx:0,ymn:24} +for(i=0;ivobj.ymx) +vobj.ymx=u.ymx +if(u.ymn=0){if(st==v_st.st1) +s.multi=-1 +else if(st==v_st.st2) +s.multi=1 +continue} +if(st_v.length<=1){if(s.floating) +s.multi=st==voice_tb[v].st?-1:1 +continue} +rvoice=sy.voices[v].range +for(i=st_v.length;--i>=0;){if(st_v[i].v==rvoice) +break} +if(i<0) +continue +if(i==st_v.length-1){s.multi=-1}else{s.multi=1 +if(i&&i+2==st_v.length){if(st_v[i].ymn-s.fmt.stemheight>=st_v[i+1].ymx) +s.multi=-1;t=s.ts_next +if(s.ts_prev&&s.ts_prev.time==s.time&&s.ts_prev.st==s.st&&s.notes[s.nhd].pit==s.ts_prev.notes[0].pit&&s.beam_st&&s.beam_end&&(!t||t.st!=s.st||t.time!=s.time)) +s.multi=-1}}} +while(s&&s.type==C.BAR) +s=s.ts_next}} +function set_rest_offset(){var s,s2,v,end_time,not_alone,v_s,y,ymax,ymin,shift,dots,dx,v_s_tb=[],sy=cur_sy +for(s=tsfirst;s;s=s.ts_next){if(s.invis) +continue +if(s.type==C.STAVES) +sy=s.sy +if(!s.dur) +continue +v_s=v_s_tb[s.v] +if(!v_s){v_s={} +v_s_tb[s.v]=v_s} +v_s.s=s;v_s.st=s.st;v_s.end_time=s.time+s.dur +if(s.type!=C.REST) +continue +ymin=-127;ymax=127;not_alone=dots=false +for(v=0;v<=v_s_tb.length;v++){v_s=v_s_tb[v] +if(!v_s||!v_s.s||v_s.st!=s.st||v==s.v) +continue +if(v_s.end_time<=s.time) +continue +not_alone=true;s2=v_s.s +if(sy.voices[v].rangeymin){ymin=s2.ymx +if(s2.dots) +dots=true}}else{if(s2.y>ymin) +ymin=s2.y}}} +end_time=s.time+s.dur +for(s2=s.ts_next;s2;s2=s2.ts_next){if(s2.time>=end_time) +break +if(s2.st!=s.st||!s2.dur||s2.invis) +continue +not_alone=true +if(sy.voices[s2.v].rangeymin){ymin=s2.ymx +if(s2.dots) +dots=true}}else{if(s2.y>ymin) +ymin=s2.y}}} +if(!not_alone){s.y=12;s.ymx=24;s.ymn=0 +continue} +if(ymax==127&&s.y<12){shift=12-s.y +s.y+=shift;s.ymx+=shift;s.ymn+=shift} +if(ymin==-127&&s.y>12){shift=s.y-12 +s.y-=shift;s.ymx-=shift;s.ymn-=shift} +shift=ymax-s.ymx +if(shift<0){shift=Math.ceil(-shift/6)*6 +if(s.ymn-shift>=ymin){s.y-=shift;s.ymx-=shift;s.ymn-=shift +continue} +dx=dots?15:10;s.notes[0].shhd=dx;s.xmx=dx +continue} +shift=ymin-s.ymn +if(shift>0){shift=Math.ceil(shift/6)*6 +if(s.ymx+shift<=ymax){s.y+=shift;s.ymx+=shift;s.ymn+=shift +continue} +dx=dots?15:10;s.notes[0].shhd=dx;s.xmx=dx +continue}}} +function new_sym(s,p_v,last_s){s.p_v=p_v +s.v=p_v.v +s.st=p_v.st +s.time=last_s.time +if(p_v.last_sym){s.next=p_v.last_sym.next +if(s.next) +s.next.prev=s;p_v.last_sym.next=s;s.prev=p_v.last_sym} +p_v.last_sym=s;lktsym(s,last_s)} +function init_music_line(){var p_voice,s,s1,s2,s3,last_s,v,st,shr,shrmx,shl,shlp,p_st,top,nv=voice_tb.length,fmt=tsfirst.fmt +for(v=0;v=0&&s.a_meter.length +break} +if(s.part) +s.next.part=s.part +unlksym(s) +case C.TEMPO:case C.BLOCK:case C.REMARK:continue} +break}} +last_s=tsfirst +for(v=0;v0){s.nflags+=s.ntrem}else{if(s.nflags<=-2){s.stemless=true +s.prev.stemless=true} +s.nflags=s.ntrem} +s.prev.nflags=s.nflags} +for(s=p_voice.sym;s;s=s.next){if(s.type==C.NOTE){pitch=s.notes[0].pit +break}} +for(s=p_voice.sym;s;s=s.next){if(s.a_gch) +self.gch_build(s) +switch(s.type){case C.MREST:start_flag=true +break +case C.BAR:res=s.fmt.bardef[s.bar_type] +if(res) +s.bar_type=res +if(!s.beam_on) +start_flag=true +if(!s.next&&s.prev&&!s.invis&&s.prev.head==C.OVALBARS) +s.prev.head=C.SQUARE +break +case C.GRACE:for(s2=s.extra;s2;s2=s2.next){s2.notes.sort(abc2svg.pitcmp) +res=identify_note(s2,s2.dur_orig) +s2.head=res[0] +s2.dots=res[1] +s2.nflags=res[2] +if(s2.trem2&&(!s2.next||s2.next.trem2)) +trem_adj(s2)} +break +case C.NOTE:case C.REST:res=identify_note(s,s.dur_orig);s.head=res[0];s.dots=res[1];s.nflags=res[2] +if(s.nflags<=-2) +s.stemless=true +if(s.xstem) +s.nflags=0 +if(s.trem1){if(s.nflags>0) +s.nflags+=s.ntrem +else +s.nflags=s.ntrem} +if(s.next&&s.next.trem2) +break +if(s.trem2){trem_adj(s) +break} +nflags=s.nflags +if(s.ntrem) +nflags+=s.ntrem +if(s.type==C.REST&&s.beam_end&&!s.beam_on){start_flag=true} +if(start_flag||nflags<=0){if(lastnote){lastnote.beam_end=true;lastnote=null} +if(nflags<=0){s.beam_st=s.beam_end=true}else if(s.type==C.NOTE||s.beam_on){s.beam_st=true;start_flag=false}} +if(s.beam_end) +start_flag=true +if(s.type==C.NOTE||s.beam_on) +lastnote=s +break} +if(s.type==C.NOTE){if(s.nhd) +s.notes.sort(abc2svg.pitcmp) +pitch=s.notes[0].pit +for(s2=s.prev;s2;s2=s2.prev){if(s2.type!=C.REST) +break +s2.notes[0].pit=pitch}}else{if(!s.notes){s.notes=[] +s.notes[0]={} +s.nhd=0} +s.notes[0].pit=pitch}} +if(lastnote) +lastnote.beam_end=true} +function set_rb(p_voice){var s2,n,s=p_voice.sym +while(s){if(s.type!=C.BAR||!s.rbstart||s.norepbra){s=s.next +continue} +n=0;s2=null +for(s=s.next;s;s=s.next){if(s.type!=C.BAR) +continue +if(s.rbstop) +break +if(!s.next){s.rbstop=2 +break} +n++ +if(n==s.fmt.rbmin) +s2=s +if(n==s.fmt.rbmax){if(s2) +s=s2;s.rbstop=1 +break}}}} +var delpit=[0,-7,-14,0] +function set_global(){var p_voice,v,nv=voice_tb.length,sy=cur_sy,st=sy.nstaff +insert_meter=cfmt.writefields.indexOf('M')>=0 +while(1){sy=sy.next +if(!sy) +break +if(sy.nstaff>st) +st=sy.nstaff} +nstaff=st;check_end_bar() +for(v=0;v1){set_float() +if(glovar.mrest_p) +mrest_expand()} +if(glovar.ottava&&cfmt.sound!="play") +set_ottava();set_clefs();self.set_pitch(null)} +function get_lshift(){var st,v,p_v,p1,po,fnt,w,sy=cur_sy,lsh1=0,lsho=0,nv=voice_tb.length +function get_wx(p,wx){var w,j,i=0 +p+='\n' +while(1){j=p.indexOf("\n",i) +if(j<0) +break +w=strwh(p.slice(i,j))[0]+12 +if(w>wx) +wx=w +if(j<0) +break +i=j+1} +return wx} +for(v=0;vlsh1) +lsh1=w} +if(po){w=get_wx(po,lsho) +if(w>lsho) +lsho=w}} +w=0 +while(sy){for(st=0;st<=sy.nstaff;st++){if(sy.staves[st].flags&(OPEN_BRACE2|OPEN_BRACKET2)){w=12 +break} +if(sy.staves[st].flags&(OPEN_BRACE|OPEN_BRACKET)) +w=6} +if(w==12) +break +sy=sy.next} +lsh1+=w +lsho+=w +return[lsh1,lsho]} +function set_indent(lsh){var st,v,w,p_voice,p,i,j,font,vnt=0,fmt=tsnext?tsnext.fmt:cfmt +if(fmt.systnames){for(v=voice_tb.length;--v>=0;){p_voice=voice_tb[v] +if(!cur_sy.voices[v]||!gene.st_print[p_voice.st]) +continue +if(p_voice.nm&&(p_voice.new_name||fmt.systnames==2)){vnt=2 +break} +if(p_voice.snm) +vnt=1}} +gene.vnt=vnt +return vnt==2?lsh[0]:lsh[1]} +function set_beams(sym){var s,t,g,beam,s_opp,n,m,mid_p,pu,pd,laststem=-1 +for(s=sym;s;s=s.next){if(s.type!=C.NOTE){if(s.type!=C.GRACE) +continue +g=s.extra +if(g.stem==2){s_opp=s +continue} +if(!s.stem) +s.stem=s.multi||1 +for(;g;g=g.next){g.stem=s.stem;g.multi=s.multi} +continue} +if(!s.stem&&s.multi) +s.stem=s.multi +if(!s.stem){mid_p=s.mid/3+18 +if(beam){s.stem=laststem}else if(s.beam_st&&!s.beam_end){beam=true;pu=s.notes[s.nhd].pit;pd=s.notes[0].pit +for(g=s.next;g;g=g.next){if(g.type!=C.NOTE) +continue +if(g.stem||g.multi) +s.stem=g.stem||g.multi +if(g.notes[g.nhd].pit>pu) +pu=g.notes[g.nhd].pit +if(g.notes[0].pitmid_p*2){s.stem=-1}else{if(s.fmt.bstemdown) +s.stem=-1}} +if(!s.stem) +s.stem=laststem}else{n=(s.notes[s.nhd].pit+s.notes[0].pit)/2 +if(n==mid_p&&s.nhd>1){for(m=0;m=mid_p) +break} +n=m*2mid_p||s.fmt.bstemdown) +s.stem=-1 +else +s.stem=laststem}}else{if(s.beam_st&&!s.beam_end) +beam=true} +if(s.beam_end) +beam=false;laststem=s.stem;if(s_opp){for(g=s_opp.extra;g;g=g.next) +g.stem=-laststem;s_opp.stem=-laststem;s_opp=null}}} +function same_head(s1,s2){var i1,i2,l1,l2,head,i11,i12,i21,i22,sh1,sh2,shu=s1.fmt.shiftunison||0 +if(shu>=3) +return false +if((l1=s1.dur)>=C.BLEN) +return false +if((l2=s2.dur)>=C.BLEN) +return false +if(s1.stemless&&s2.stemless) +return false +if(s1.dots!=s2.dots){if(shu&1||s1.dots*s2.dots!=0) +return false} +if(s1.stem*s2.stem>0) +return false +i1=i2=0 +if(s1.notes[0].pit>s2.notes[0].pit){if(s1.stem<0) +return false +while(s2.notes[i2].pit!=s1.notes[0].pit){if(++i2>s2.nhd) +return false}}else if(s1.notes[0].pits1.nhd) +return false}} +if(s2.notes[i2].acc!=s1.notes[i1].acc) +return false;i11=i1;i21=i2;sh1=s1.notes[i1].shhd;sh2=s2.notes[i2].shhd +do{i1++;i2++ +if(i1>s1.nhd){break} +if(i2>s2.nhd){break} +if(s2.notes[i2].acc!=s1.notes[i1].acc) +return false +if(sh10) +return false}else if(i2<=s2.nhd){if(s1.stem>0) +return false} +i12=i1;i22=i2;head=0 +if(l1!=l2){if(l1=C.BLEN/2?2:1}else{return false}} +if(!head) +head=s1.p_v.scale-2){if(s.stem>0){w=-w;i=s.notes[0].pit*2;j=(Math.ceil((s.ymx-2)/3)+18)*2}else{i=(Math.ceil((s.ymn+2)/3)+18)*2;j=s.notes[s.nhd].pit*2} +if(i<0) +i=0 +if(j>=MAXPIT) +j=MAXPIT-1 +while(i<=j) +left[i++]=w} +shift=s.notes[s.stem>0?0:s.nhd].shhd;for(m=0;m<=s.nhd;m++){w=-s.notes[m].shhd+w_base+shift;i=s.notes[m].pit*2 +if(i<0) +i=0 +else if(i>=MAXPIT-1) +i=MAXPIT-2 +if(w>left[i]) +left[i]=w +if(s.head!=C.SQUARE) +w-=1 +if(w>left[i-1]) +left[i-1]=w +if(w>left[i+1]) +left[i+1]=w} +return left} +function set_right(s){var m,i,j,k,shift,w_base=w_note[s.head],w=w_base,flags=s.nflags>0&&s.beam_st&&s.beam_end,right=[] +for(i=0;i-2){if(s.stem<0){w=-w;i=(Math.ceil((s.ymn+2)/3)+18)*2;j=s.notes[s.nhd].pit*2;k=i+4}else{i=s.notes[0].pit*2;j=(Math.ceil((s.ymx-2)/3)+18)*2} +if(i<0) +i=0 +if(j>MAXPIT) +j=MAXPIT +while(i0){if(s.xmx==0) +i=s.notes[s.nhd].pit*2 +else +i=s.notes[0].pit*2;i+=4 +if(i<0) +i=0 +for(;i0?0:s.nhd].shhd +for(m=0;m<=s.nhd;m++){w=s.notes[m].shhd+w_base-shift;i=s.notes[m].pit*2 +if(i<0) +i=0 +else if(i>=MAXPIT-1) +i=MAXPIT-2 +if(w>right[i]) +right[i]=w +if(s.head!=C.SQUARE) +w-=1 +if(w>right[i-1]) +right[i-1]=w +if(w>right[i+1]) +right[i+1]=w} +return right} +function set_overlap(){var s,s1,s2,s3,i,i1,i2,m,sd,t,dp,d,d2,dr,dr2,dx,left1,right1,left2,right2,right3,pl,pr,sy=cur_sy +function v_invert(){s1=s2;s2=s;d=d2;pl=left1;pr=right1;dr2=dr} +for(s=tsfirst;s;s=s.ts_next){if(s.type!=C.NOTE||s.invis){if(s.type==C.STAVES) +sy=s.sy +continue} +if(s.xstem&&s.ts_prev.stem<0){for(m=0;m<=s.nhd;m++){s.notes[m].shhd-=7;s.notes[m].shac+=16}} +s2=s +while(1){s2=s2.ts_next +if(!s2) +break +if(s2.time!=s.time){s2=null +break} +if(s2.type==C.NOTE&&!s2.invis&&s2.st==s.st) +break} +if(!s2) +continue +s1=s +if(sy.voices[s1.v].ranges2.ymx||s1.ymx0&&s2.stem<0&&s1.notes[0].pit==s2.notes[s2.nhd].pit+1)||(s1.stem<0&&s2.stem>0&&s1.notes[s1.nhd].pit+1==s2.notes[0].pit)){if(s1.stem<0){s1=s2;s2=s} +d=s1.notes[0].shhd+7 +for(m=0;m<=s2.nhd;m++) +s2.notes[m].shhd+=d +s2.xmx+=d +s1.xmx=s2.xmx +continue} +right1=set_right(s1);left2=set_left(s2);s3=s1.ts_prev +if(s3&&s3.time==s1.time&&s3.st==s1.st&&s3.type==C.NOTE&&!s3.invis){right3=set_right(s3) +for(i=0;iright1[i]) +right1[i]=right3[i]}}else{s3=null} +d=-10 +for(i=0;id) +d=left2[i]+right1[i]} +if(d<-3&&((s2.notes[0].pit&1)||!(s1.dots||s2.dots)||(!(s1.notes[s1.nhd].pit==s2.notes[0].pit+2&&s1.dot_low)&&!(s1.notes[s1.nhd].pit+2==s2.notes[0].pit&&s2.dot_low)))) +continue +right2=set_right(s2);left1=set_left(s1) +if(s3){right3=set_left(s3) +for(i=0;ileft1[i]) +left1[i]=right3[i]}} +d2=dr=dr2=-100 +for(i=0;id2) +d2=left1[i]+right2[i] +if(right2[i]>dr2) +dr2=right2[i] +if(right1[i]>dr) +dr=right1[i]} +t=0;i1=s1.nhd;i2=s2.nhd +while(1){dp=s1.notes[i1].pit-s2.notes[i2].pit +switch(dp){case 2:if(!(s1.notes[i1].pit&1)) +s1.dot_low=false +break +case 1:if(s1.notes[i1].pit&1) +s2.dot_low=true +else +s1.dot_low=false +break +case 0:if(s1.notes[i1].acc!=s2.notes[i2].acc){t=-1 +break} +if(s2.notes[i2].acc){if(!s1.notes[i1].acc) +s1.notes[i1].acc=s2.notes[i2].acc +s2.notes[i2].acc=0} +if(s1.dots&&s2.dots&&(s1.notes[i1].pit&1)) +t=1 +break +case-1:if(s1.notes[i1].pit&1) +s2.dot_low=false +else +s1.dot_low=true +break +case-2:if(!(s1.notes[i1].pit&1)) +s2.dot_low=false +break} +if(t<0) +break +if(dp>=0){if(--i1<0) +break} +if(dp<=0){if(--i2<0) +break}} +if(t<0){unison_acc(s1,s2,i1,i2) +continue} +sd=0;if(s1.dots){if(s2.dots){if(!t) +sd=1}else{v_invert()}}else if(s2.dots){if(d2+dr=0?0:s1.nhd;d+=s1.notes[m].shhd;m=s2.stem>=0?0:s2.nhd;d-=s2.notes[m].shhd +if(s1.dots){dx=7.7+s1.xmx+ +3.5*s1.dots-3.5+ +3;if(!sd){d2=-100;for(i1=0;i1<=s1.nhd;i1++){i=s1.notes[i1].pit +if(!(i&1)){if(!s1.dot_low) +i++ +else +i--} +i*=2 +if(i<1) +i=1 +else if(i>=MAXPIT-1) +i=MAXPIT-2 +if(pl[i]>d2) +d2=pl[i] +if(pl[i-1]+1>d2) +d2=pl[i-1]+1 +if(pl[i+1]+1>d2) +d2=pl[i+1]+1} +if(dx+d2+2>d) +d=dx+d2+2}else{if(dx=MAXPIT-1) +i=MAXPIT-2 +if(pr[i]>d2) +d2=pr[i] +if(pr[i-1]+1>d2) +d2=pr[i-1]=1 +if(pr[i+1]+1>d2) +d2=pr[i+1]+1} +if(d2>4.5&&7.7+s1.xmx+2=0;m--){s2.notes[m].shhd+=d} +s2.xmx+=d +if(sd) +s1.xmx=s2.xmx}} +Abc.prototype.set_stems=function(){var s,s2,g,slen,scale,ymn,ymx,nflags,ymin,ymax +for(s=tsfirst;s;s=s.ts_next){if(s.type!=C.NOTE){if(s.type!=C.GRACE) +continue +ymin=ymax=s.mid +for(g=s.extra;g;g=g.next){slen=GSTEM +if(g.nflags>1) +slen+=1.2*(g.nflags-1);ymn=3*(g.notes[0].pit-18);ymx=3*(g.notes[g.nhd].pit-18) +if(s.stem>=0){g.y=ymn;g.ys=ymx+slen;ymx=Math.round(g.ys)}else{g.y=ymx;g.ys=ymn-slen;ymn=Math.round(g.ys)} +ymx+=4 +ymn-=4 +if(ymnymax) +ymax=ymx;g.ymx=ymx;g.ymn=ymn} +s.ymx=ymax;s.ymn=ymin +continue} +set_head_shift(s);nflags=s.nflags +if(s.beam_st&&!s.beam_end){if(s.feathered_beam) +nflags=++s.nflags +for(s2=s.next;;s2=s2.next){if(s2.type==C.NOTE){if(s.feathered_beam) +s2.nflags++ +if(s2.beam_end) +break}} +if(s2.nflags>nflags) +nflags=s2.nflags}else if(!s.beam_st&&s.beam_end){for(s2=s.prev;;s2=s2.prev){if(s2.beam_st) +break} +if(s2.nflags>nflags) +nflags=s2.nflags} +slen=s.fmt.stemheight +switch(nflags){case 2:slen+=0;break +case 3:slen+=4;break +case 4:slen+=8;break +case 5:slen+=12;break} +if((scale=s.p_v.scale)!=1) +slen*=(scale+1)*.5;ymn=3*(s.notes[0].pit-18) +if(s.nhd>0){slen-=2;ymx=3*(s.notes[s.nhd].pit-18)}else{ymx=ymn} +if(s.ntrem) +slen+=2*s.ntrem +if(s.stemless){if(s.stem>=0){s.y=ymn;s.ys=ymx}else{s.ys=ymn;s.y=ymx} +s.ymx=ymx+4;s.ymn=ymn-4}else if(s.stem>=0){if(s.notes[s.nhd].pit>26&&(nflags<=0||!s.beam_st||!s.beam_end)){slen-=2 +if(s.notes[s.nhd].pit>28) +slen-=2} +s.y=ymn +if(s.notes[0].tie) +ymn-=3;s.ymn=ymn-4;s.ys=ymx+slen +if(s.yss.mid) +s.ys=s.mid;s.ymn=(s.ys-2.5)|0;s.y=ymx +if(s.notes[s.nhd].tie) +ymx+=3;s.ymx=ymx+4}}} +var blocks=[] +Abc.prototype.block_gen=function(s){switch(s.subtype){case"leftmargin":case"rightmargin":case"pagescale":case"pagewidth":case"scale":case"staffwidth":self.set_format(s.subtype,s.param) +break +case"mc_start":if(multicol){error(1,s,"No end of the previous %%multicol") +break} +multicol={posy:posy,maxy:posy,lm:cfmt.leftmargin,rm:cfmt.rightmargin,w:cfmt.pagewidth,sc:cfmt.scale} +break +case"mc_new":if(!multicol){error(1,s,"%%multicol new without start") +break} +if(posy>multicol.maxy) +multicol.maxy=posy +cfmt.leftmargin=multicol.lm +cfmt.rightmargin=multicol.rm +cfmt.pagewidth=multicol.w +cfmt.scale=multicol.sc +posy=multicol.posy +img.chg=1 +break +case"mc_end":if(!multicol){error(1,s,"%%multicol end without start") +break} +if(posy') +blkdiv=2 +break +case"sep":set_page();vskip(s.sk1);output+='\n';vskip(s.sk2);break +case"text":set_font(s.font) +use_font(s.font) +write_text(s.text,s.opt) +break +case"title":write_title(s.text,true) +break +case"vskip":vskip(s.sk);break}} +function sym_staff_move(st){for(var s=tsfirst;s;s=s.ts_next){if(s.nl) +break +if(s.st==st&&s.type!=C.CLEF){s.st++ +if(s.type!=C.TEMPO) +s.invis=true}}} +function set_piece(){var s,last,p_voice,st,v,nv,tmp,non_empty,non_empty_gl=[],sy=cur_sy +function reset_staff(st){var p_staff=staff_tb[st],sy_staff=sy.staves[st] +if(!p_staff) +p_staff=staff_tb[st]={} +p_staff.y=0;p_staff.stafflines=sy_staff.stafflines;p_staff.staffscale=sy_staff.staffscale;p_staff.ann_top=p_staff.ann_bot=0} +function set_brace(){var st,i,empty_fl,n=sy.staves.length +for(st=0;st=l-2){if(p_staff.stafflines[i]!='.'){p_staff.botbar-=6;p_staff.topbar+=6}else{p_staff.botbar-=12;p_staff.topbar+=12 +continue}} +if(!non_empty_gl[st]) +continue +p_staff.hll=17+i*2 +p_staff.hlmap=new Int8Array((l-i+1)*2 ++2) +for(j=1;instaff){switch(s.type){case C.CLEF:staff_tb[st].clef=s +break +case C.KEY:s.p_v.ckey=s +break +case C.METER:s.p_v.meter=s +break} +unlksym(s) +continue} +if(non_empty[st]) +continue +switch(s.type){default:continue +case C.BAR:if(s.bar_mrep||sy.staves[st].staffnonote>1) +break +continue +case C.GRACE:break +case C.NOTE:case C.REST:case C.SPACE:case C.MREST:if(sy.staves[st].staffnonote>1) +break +if(s.invis) +continue +if(sy.staves[st].staffnonote||s.type==C.NOTE) +break +continue} +non_empty_gl[st]=non_empty[st]=true} +tsnext=s;set_brace() +sy.st_print=non_empty +set_top_bot() +for(st=0;stwidth){error(1,s,"Line too much shrunk $1 $2 $3",xmin.toFixed(1),xx.toFixed(1),width.toFixed(1)) +break} +if(s.space){if(s.space=width){x=0 +for(;s;s=s.ts_next){if(s.seqst) +x+=s.shrink;s.x=x} +spf_last=0}else if((xx0+xx)/2+xs>width||stretch){while(--cnt>=0){if(xx==xse) +xx+=5 +spf=(width-xs-xse)/(xx-xse) +xx=0;xse=0;x=0 +for(s=tsfirst;s;s=s.ts_next){if(s.seqst){if(s.space){if(s.space*spf<=s.shrink){xse+=s.shrink;xx+=s.shrink;x+=s.shrink}else{xx+=s.space;x+=s.space*spf}}else{x+=s.shrink}} +s.x=x} +if(Math.abs(x-width)<0.1) +break} +spf_last=width/(xx+xs)}else{spf=spf_last +if(spf<1-s.fmt.maxshrink) +spf=1-s.fmt.maxshrink +if(ll&&spf(width-xs)/xx) +spf=(width-xs)/xx +x=0 +for(;s;s=s.ts_next){if(s.seqst) +x+=s.space?(s.space*spf<=s.shrink?s.shrink:s.space*spf):s.shrink +s.x=x}} +realwidth=x +for(s=some_grace;s;s=s.ts_next){if(s.type!=C.GRACE) +continue +if(s.gr_shift) +x=s.prev.x+s.prev.wr +else +x=s.x-s.wl +for(g=s.extra;g;g=g.next) +g.x+=x}} +function set_sym_line(){var p_v,s,v=voice_tb.length +while(--v>=0){p_v=voice_tb[v] +if(p_v.sym&&p_v.s_prev){p_v.sym.prev=p_v.s_prev +p_v.s_prev.next=p_v.sym} +s=p_v.s_next +p_v.s_next=null +p_v.sym=s +if(s){if(s.prev) +s.prev.next=s +p_v.s_prev=s.prev +s.prev=null}else{p_v.s_prev=null}}} +function set_posx(){posx=img.lm/cfmt.scale} +function gen_init(){var s=tsfirst,tim=s.time +for(;s;s=s.ts_next){if(s.time!=tim){set_page() +return} +switch(s.type){case C.NOTE:case C.REST:case C.MREST:case C.SPACE:set_page() +return +default:continue +case C.STAVES:cur_sy=s.sy +continue +case C.BLOCK:if(s.play) +continue +self.block_gen(s) +break} +unlksym(s) +if(s.p_v.s_next==s) +s.p_v.s_next=s.next} +tsfirst=null} +Abc.prototype.output_music=function(){var v,lwidth,indent,lsh,line_height,ts1st,tslast,p_v,nv=voice_tb.length +set_global() +if(nv>1) +self.set_stem_dir() +for(v=0;v1){set_rest_offset();set_overlap()} +set_allsymwidth(1) +gen_init() +if(!tsfirst) +return +lsh=get_lshift() +if(cfmt.singleline){v=get_ck_width();lwidth=lsh[0]+v[0]+v[1]+get_width(tsfirst,null)[0] +v=cfmt.singleline==2?get_lwidth():lwidth +if(v>lwidth) +lwidth=v +else +img.width=lwidth*cfmt.scale+img.lm+img.rm+2}else{lwidth=get_lwidth();cut_tune(lwidth,lsh)} +ts1st=tsfirst +v=nv +while(--v>=0) +voice_tb[v].osym=voice_tb[v].sym +spf_last=0 +while(1){set_piece();indent=set_indent(lsh) +if(!line_height&&cfmt.indent&&indent=0){p_v=voice_tb[v] +if(p_v.sym&&p_v.s_prev) +p_v.sym.prev=p_v.s_prev +p_v.sym=p_v.osym}} +var a_gch,a_dcn=[],multicol,maps={} +var qplet_tb=new Int8Array([0,1,3,2,3,0,2,0,3,0]),ntb="CDEFGABcdefgab" +function set_ref(s){s.fname=parse.fname;s.istart=parse.istart;s.iend=parse.iend} +function new_clef(clef_def){var s={type:C.CLEF,clef_line:2,clef_type:"t",v:curvoice.v,p_v:curvoice,time:curvoice.time,dur:0},i=1 +set_ref(s) +switch(clef_def[0]){case'"':i=clef_def.indexOf('"',1);s.clef_name=clef_def.slice(1,i);i++ +break +case'a':if(clef_def[1]=='u'){s.clef_type="a";s.clef_auto=true;i=4 +break} +i=4 +case'C':s.clef_type="c";s.clef_line=3 +break +case'b':i=4 +case'F':s.clef_type="b";s.clef_line=4 +break +case'n':i=4 +s.invis=true +s.clef_none=1 +break +case't':if(clef_def[1]=='e'){s.clef_type="c";s.clef_line=4 +break} +i=6 +case'G':break +case'p':i=4 +case'P':s.clef_type="p";s.clef_line=3;break +default:syntax(1,"Unknown clef '$1'",clef_def) +return} +if(clef_def[i]>='1'&&clef_def[i]<='9'){s.clef_line=+clef_def[i] +i++} +delete curvoice.snd_oct +if(clef_def[i+1]!='8'&&clef_def[i+1]!='1') +return s +switch(clef_def[i]){case'^':s.clef_oct_transp=true +case'+':s.clef_octave=clef_def[i+1]=='8'?7:14 +if(!s.clef_oct_transp) +curvoice.snd_oct=clef_def[i+1]==8?12:24 +break +case'_':s.clef_oct_transp=true +case'-':s.clef_octave=clef_def[i+1]=='8'?-7:-14 +if(!s.clef_oct_transp) +curvoice.snd_oct=clef_def[i+1]==8?-12:-24 +break} +return s} +function get_interval(param,score){var i,val,tmp,note,pit +tmp=new scanBuf;tmp.buffer=param +pit=[] +for(i=0;i<2;i++){note=tmp.buffer[tmp.index]?parse_acc_pit(tmp):null +if(!note){if(i!=1||!score){syntax(1,errs.bad_transp) +return} +pit[i]=242}else{if(typeof note.acc=="object"){syntax(1,errs.bad_transp) +return} +pit[i]=abc2svg.pab40(note.pit,note.acc)}} +return pit[1]-pit[0]} +function nt_trans(nt,a){var ak,an,d,b40,n +if(typeof a=="object"){n=a[0] +d=a[1] +a=n>0?1:-1} +b40=abc2svg.pab40(nt.pit,a) ++curvoice.tr_sco +nt.pit=abc2svg.b40p(b40) +an=abc2svg.b40a(b40) +if(!d){if(an==-3) +return an +a=an +if(nt.acc){if(!a) +a=3}else{if(!curvoice.ckey.k_none) +a=0} +nt.acc=a +return an} +switch(an){case-2:if(n>0) +n-=d*2 +else +n-=d +break +case-1:if(n>0) +n-=d +break +case 0:case 3:if(n>0) +n-=d +else +n+=d +break +case 1:if(n<0) +n+=d +break +case 2:if(n<0) +n+=d*2 +else +n+=d +break} +nt.acc=[n,d] +return an} +function set_linebreak(param){var i,item +for(i=0;i<128;i++){if(char_tb[i]=="\n") +char_tb[i]=nil} +param=param.split(/\s+/) +for(i=0;i":continue +case"":item='\n' +break +default:syntax(1,"Bad value '$1' in %%linebreak - ignored",item) +continue} +char_tb[item.charCodeAt(0)]='\n'}} +function set_user(parm){var k,c,v,a=parm.match(/(.)[=\s]*(\[I:.+\]|".+"|!.+!)$/) +if(!a){syntax(1,'Lack of starting [, ! or " in U: / %%user') +return} +c=a[1];v=a[2] +if(c[0]=='\\'){if(c[1]=='t') +c='\t' +else if(!c[1]) +c=' '} +k=c.charCodeAt(0) +if(k>=128){syntax(1,errs.not_ascii) +return} +switch(char_tb[k][0]){case'0':case'd':case'i':case' ':break +case'"':case'!':case'[':if(char_tb[k].length>1) +break +default:syntax(1,"Bad user character '$1'",c) +return} +switch(v){case"!beambreak!":v=" " +break +case"!ignore!":v="i" +break +case"!nil!":case"!none!":v="d" +break} +char_tb[k]=v} +function get_st_lines(param){if(!param) +return +if(/^[\]\[|.-]+$/.test(param)) +return param.replace(/\]/g,'[') +var n=+param +switch(n){case 0:return"..." +case 1:return"..|" +case 2:return".||" +case 3:return".|||"} +if(isNaN(n)||n<0||n>16) +return +return"||||||||||||||||".slice(0,n)} +function new_block(subtype){var c_v,s={type:C.BLOCK,subtype:subtype,dur:0} +c_v=curvoice +if(subtype.slice(0,4)!="midi") +curvoice=voice_tb[0] +sym_link(s) +if(c_v) +curvoice=c_v +return s} +Abc.prototype.set_vp=function(a){var s,item,pos,val,clefpit,tr_p=0 +while(1){item=a.shift() +if(!item) +break +if(item.slice(-1)=='='&&!a.length){syntax(1,errs.bad_val,item) +break} +switch(item){case"clef=":s=a.shift() +break +case"clefpitch=":item=a.shift() +if(item){val=ntb.indexOf(item[0]) +if(val>=0){switch(item[1]){case"'":val+=7 +break +case',':val-=7 +if(item[2]==',') +val-=7 +break} +clefpit=4-val +break}} +syntax(1,errs.bad_val,item) +break +case"octave=":val=+a.shift() +if(isNaN(val)) +syntax(1,errs.bad_val,item) +else +curvoice.octave=val +break +case"cue=":curvoice.scale=a.shift()=='on'?.7:1 +break +case"instrument=":item=a.shift() +val=item.indexOf('/') +if(val<0){val=get_interval('c'+item) +if(val==undefined) +break +curvoice.sound=val +tr_p|=2 +val=0}else{val=get_interval('c'+item.slice(val+1)) +if(val==undefined) +break +curvoice.sound=val +tr_p|=2 +val=get_interval(item.replace('/','')) +if(val==undefined) +break} +curvoice.score=cfmt.sound?curvoice.sound:val +tr_p|=1 +break +case"map=":curvoice.map=a.shift() +break +case"name=":case"nm=":curvoice.nm=a.shift() +if(curvoice.nm[0]=='"') +curvoice.nm=cnv_escape(curvoice.nm.slice(1,-1)) +curvoice.new_name=true +break +case"stem=":case"pos=":if(item=="pos=") +item=a.shift().slice(1,-1).split(' ') +else +item=["stm",a.shift()];val=posval[item[1]] +if(val==undefined){syntax(1,errs.bad_val,"%%pos") +break} +switch(item[2]){case"align":val|=C.SL_ALIGN;break +case"center":val|=C.SL_CENTER;break +case"close":val|=C.SL_CLOSE;break} +if(!pos) +pos={} +pos[item[0]]=val +break +case"scale=":val=+a.shift() +if(isNaN(val)||val<.5||val>2) +syntax(1,errs.bad_val,"%%voicescale") +else +curvoice.scale=val +break +case"score=":if(cfmt.nedo){syntax(1,errs.notransp) +break} +item=a.shift() +if(cfmt.sound) +break +val=get_interval(item,true) +if(val!=undefined){curvoice.score=val +tr_p|=1} +break +case"shift=":if(cfmt.nedo){syntax(1,errs.notransp) +break} +val=get_interval(a.shift()) +if(val!=undefined){curvoice.shift=val +tr_p=3} +break +case"sound=":if(cfmt.nedo){syntax(1,errs.notransp) +break} +val=get_interval(a.shift()) +if(val==undefined) +break +curvoice.sound=val +if(cfmt.sound) +curvoice.score=val +tr_p|=2 +break +case"subname=":case"sname=":case"snm=":curvoice.snm=a.shift() +if(curvoice.snm[0]=='"') +curvoice.snm=curvoice.snm.slice(1,-1);break +case"stafflines=":val=get_st_lines(a.shift()) +if(val==undefined) +syntax(1,"Bad %%stafflines value") +else if(curvoice.st!=undefined) +par_sy.staves[curvoice.st].stafflines=val +else +curvoice.stafflines=val +break +case"staffnonote=":val=+a.shift() +if(isNaN(val)) +syntax(1,"Bad %%staffnonote value") +else +curvoice.staffnonote=val +break +case"staffscale=":val=+a.shift() +if(isNaN(val)||val<.3||val>2) +syntax(1,"Bad %%staffscale value") +else +curvoice.staffscale=val +break +case"tacet=":val=a.shift() +curvoice.tacet=val||undefined +break +case"transpose=":if(cfmt.nedo){syntax(1,errs.notransp) +break} +val=get_transp(a.shift()) +if(val==undefined){syntax(1,errs.bad_transp)}else{curvoice.sound=val +if(cfmt.sound) +curvoice.score=val +tr_p=2} +break +default:switch(item.slice(0,4)){case"treb":case"bass":case"alto":case"teno":case"perc":s=item +break +default:if("GFC".indexOf(item[0])>=0) +s=item +else if(item.slice(-1)=='=') +a.shift() +break} +break}} +if(pos){curvoice.pos=clone(curvoice.pos) +for(item in pos) +if(pos.hasOwnProperty(item)) +curvoice.pos[item]=pos[item]} +if(s){s=new_clef(s) +if(s){if(clefpit) +s.clefpit=clefpit +get_clef(s)}} +if(tr_p&2){tr_p=(curvoice.sound|0)+(curvoice.shift|0) +if(tr_p) +curvoice.tr_snd=abc2svg.b40m(tr_p+122)-36 +else if(curvoice.tr_snd) +curvoice.tr_snd=0}} +function set_kv_parm(a){if(!curvoice.init){curvoice.init=true +if(info.V){if(info.V[curvoice.id]) +a=info.V[curvoice.id].concat(a) +if(info.V['*']) +a=info.V['*'].concat(a)}} +if(a.length) +self.set_vp(a)} +function memo_kv_parm(vid,a){if(!a.length) +return +if(!info.V) +info.V={} +if(info.V[vid]) +Array.prototype.push.apply(info.V[vid],a) +else +info.V[vid]=a} +function new_key(param){var i,key_end,c,tmp,note,sf="FCGDAEB".indexOf(param[0])-1,mode=0,s={type:C.KEY,dur:0} +set_ref(s);i=1 +if(sf<-1){switch(param[0]){case'H':key_end=true +if(param[1].toLowerCase()!='p'){syntax(1,"Unknown bagpipe-like key") +break} +s.k_bagpipe=param[1];sf=param[1]=='P'?0:2;i++ +if(!cfmt.temper) +cfmt.temper=new Float32Array([11.62,12.55,1.66,2.37,3.49,0,1.66,2.37,3.49,4.41,5.53,0,3.49,4.41,5.53,6.63,7.35,4.41,5.53,6.63,7.35,8.19,0,6.63,7.35,8.19,9.39,10.51,0,8.19,9.39,10.51,11.62,12.55,0,10.51,11.62,12.55,1.66,1.66]) +break +case'P':syntax(1,"K:P is deprecated");sf=0;s.k_drum=true;key_end=true +break +case'n':if(param.indexOf("none")==0){sf=0;s.k_none=true;i=4 +break} +default:s.k_map=[] +s.k_mode=0 +return[s,info_split(param)]}} +if(!key_end){switch(param[i]){case'#':sf+=7;i++;break +case'b':sf-=7;i++;break} +param=param.slice(i).trim() +switch(param.slice(0,3).toLowerCase()){default:if(param[0]!='m'||(param[1]!=' '&¶m[1]!='\t'&¶m[1]!='\n')){key_end=true +break} +case"aeo":case"m":case"min":sf-=3;mode=5 +break +case"dor":sf-=2;mode=1 +break +case"ion":case"maj":break +case"loc":sf-=5;mode=6 +break +case"lyd":sf+=1;mode=3 +break +case"mix":sf-=1;mode=4 +break +case"phr":sf-=4;mode=2 +break} +if(!key_end) +param=param.replace(/\w+\s*/,'') +if(param.indexOf("exp ")==0){param=param.replace(/\w+\s*/,'') +if(!param) +syntax(1,"No accidental after 'exp'");s.exp=1} +c=param[0] +if(c=='^'||c=='_'||c=='='){s.k_a_acc=[];tmp=new scanBuf;tmp.buffer=param +do{note=parse_acc_pit(tmp) +if(!note) +break +s.k_a_acc.push(note);c=param[tmp.index] +while(c==' ') +c=param[++tmp.index]}while(c=='^'||c=='_'||c=='=');param=param.slice(tmp.index)}else if(s.exp&¶m.indexOf("none")==0){sf=0 +param=param.replace(/\w+\s*/,'')}} +if(sf<-7||sf>7){syntax(1,"Key with double sharps/flats") +if(sf>7) +sf-=12 +else +sf+=12} +s.k_sf=sf;s.k_map=s.k_bagpipe&&!sf?abc2svg.keys[9]:abc2svg.keys[sf+7] +if(s.k_a_acc){s.k_map=new Int8Array(s.k_map) +i=s.k_a_acc.length +while(--i>=0){note=s.k_a_acc[i] +s.k_map[(note.pit+19)%7]=note.acc}} +s.k_mode=mode +s.k_b40=[1,24,7,30,13,36,19,2,25,8,31,14,37,20,3][sf+7] +return[s,info_split(param)]} +function new_meter(p){var p_v,s={type:C.METER,dur:0,a_meter:[]},meter={},val,v,m1=0,m2,i=0,j,wmeasure,in_parenth;set_ref(s) +if(p.indexOf("none")==0){i=4;wmeasure=1}else{wmeasure=0 +while(i'9'){syntax(1,"Bad char '$1' in M:",p[i]) +return} +m2=2;meter.top=p[i++] +for(;;){while(p[i]>='0'&&p[i]<='9') +meter.top+=p[i++] +if(p[i]==')'){if(p[i+1]!='/') +break +i++} +if(p[i]=='/'){i++;if(p[i]<='0'||p[i]>'9'){syntax(1,"Bad char '$1' in M:",p[i]) +return} +meter.bot=p[i++] +while(p[i]>='0'&&p[i]<='9') +meter.bot+=p[i++] +break} +if(p[i]!=' '&&p[i]!='+') +break +if(i>=p.length||p[i+1]=='(') +break +meter.top+=p[i++]} +m1=+meter.top +break} +if(!in_parenth){if(meter.bot) +m2=+meter.bot +wmeasure+=m1*C.BLEN/m2} +s.a_meter.push(meter);meter={} +while(p[i]==' ') +i++ +if(p[i]=='+'){meter.top=p[i++];s.a_meter.push(meter);meter={}}}} +if(p[i]=='='){val=p.substring(++i).match(/^(\d+)\/(\d+)$/) +if(!val){syntax(1,"Bad duration '$1' in M:",p.substring(i)) +return} +wmeasure=C.BLEN*val[1]/val[2]} +if(!wmeasure){syntax(1,errs.bad_val,'M:') +return} +s.wmeasure=wmeasure +if(cfmt.writefields.indexOf('M')<0) +s.a_meter=[] +if(parse.state!=3){info.M=p;glovar.meter=s +if(parse.state){if(!glovar.ulen){if(wmeasure<=1||wmeasure>=C.BLEN*3/4) +glovar.ulen=C.BLEN/8 +else +glovar.ulen=C.BLEN/16} +for(v=0;v0){d=text.slice(0,i).split(/\s+/) +text=text.slice(i+1).replace(/^\s+/,'') +while(1){c=d.shift() +if(!c) +break +nd=get_nd(c) +if(!nd) +return +if(!s.tempo_notes) +s.tempo_notes=[] +s.tempo_notes.push(nd)} +if(text.slice(0,4)=="ca. "){s.tempo_ca='ca. ' +text=text.slice(4)} +i=text.indexOf('/') +if(i>0){nd=get_nd(text) +if(!nd) +return +s.new_beat=nd}else{s.tempo=+text +if(!s.tempo||isNaN(s.tempo)){syntax(1,"Bad tempo value") +return}}} +if(parse.state<2||(!curvoice.time&&!glovar.tempo)){info.Q=txt +glovar.tempo=s +return} +if(!glovar.tempo) +syntax(0,"No previous tempo") +if(new_ctrl(s)) +sym_link(s)} +function do_info(info_type,text){var s,d1,d2,a,vid,tim,v,p_v +if(curvoice&&curvoice.ignore){switch(info_type){default:return +case'P':case'Q':case'V':break}} +switch(info_type){case'I':self.do_pscom(text) +break +case'L':a=text.match(/^1\/(\d+)(=(\d+)\/(\d+))?$/) +if(a){d1=+a[1] +if(!d1||(d1&(d1-1))!=0) +break +d1=C.BLEN/d1 +if(a[2]){d2=+a[4] +d2=d2?+a[3]/d2*C.BLEN:0}else{d2=d1}}else if(text=="auto"){d1=d2=-1} +if(!d2){syntax(1,"Bad L: value") +break} +if(parse.state<=1){glovar.ulen=d1}else{curvoice.ulen=d1;curvoice.dur_fact=d2/d1} +break +case'M':new_meter(text) +break +case'U':set_user(text) +break +case'P':if(!parse.state) +break +if(parse.state==1){info.P=text +break} +s={type:C.PART,text:text,time:tim} +if(!new_ctrl(s)) +break +sym_link(s) +if(cfmt.writefields.indexOf(info_type)<0) +s.invis=true +break +case'Q':if(!parse.state) +break +new_tempo(text) +break +case'V':get_voice(text) +if(parse.state==3) +curvoice.ignore=!par_sy.voices[curvoice.v] +break +case'K':if(!parse.state) +break +get_key(text) +break +case'N':case'R':if(!info[info_type]) +info[info_type]=text +else +info[info_type]+='\n'+text +break +case'r':if(!user.keep_remark||parse.state!=3) +break +s={type:C.REMARK,text:text,dur:0} +sym_link(s) +break +default:syntax(0,"'$1:' line ignored",info_type) +break}} +function adjust_dur(s){var s2,time,auto_time,i,fac;s2=curvoice.last_sym +if(!s2) +return;if(s2.type==C.MREST||s2.type==C.BAR) +return +while(s2.type!=C.BAR&&s2.prev) +s2=s2.prev;time=s2.time;auto_time=curvoice.time-time +fac=curvoice.wmeasure/auto_time +if(fac==1) +return +for(;s2;s2=s2.next){s2.time=time +if(!s2.dur||s2.grace) +continue +s2.dur*=fac;s2.dur_orig*=fac;time+=s2.dur +if(s2.type!=C.NOTE&&s2.type!=C.REST) +continue +for(i=0;i<=s2.nhd;i++) +s2.notes[i].dur*=fac} +curvoice.time=s.time=time} +function new_bar(){var s2,c,bar_type,line=parse.line,s={type:C.BAR,fname:parse.fname,istart:parse.bol+line.index,dur:0,multi:0} +if(vover&&vover.bar) +get_vover('|') +if(glovar.new_nbar){s.bar_num=glovar.new_nbar;glovar.new_nbar=0} +bar_type=line.char() +while(1){c=line.next_char() +switch(c){case'|':case'[':case']':case':':bar_type+=c +continue} +break} +if(bar_type[0]==':'){if(bar_type==':'){bar_type='|';s.bar_dotted=true}else{s.rbstop=2}} +if(a_gch) +csan_add(s) +if(a_dcn.length) +deco_cnv(s) +if(bar_type.slice(-1)=='['&&!(/[0-9" ]/.test(c))){bar_type=bar_type.slice(0,-1);line.index--;c='['} +if(c>'0'&&c<='9'){s.text=c +while(1){c=line.next_char() +if("0123456789,.-".indexOf(c)<0) +break +s.text+=c}}else if(c=='"'&&bar_type.slice(-1)=='['){s.text="" +while(1){c=line.next_char() +if(!c){syntax(1,"No end of repeat string") +return} +if(c=='"'){line.index++ +break} +s.text+=c}} +if(bar_type[0]==']'){s.rbstop=2 +if(bar_type.length!=1) +bar_type=bar_type.slice(1) +else +s.invis=true} +s.iend=parse.bol+line.index +if(s.text&&bar_type.slice(-1)=='['&&bar_type!='[') +bar_type=bar_type.slice(0,-1) +if(bar_type.slice(-1)==':'){s.rbstop=1 +if(s.text){syntax(1,"Variant ending on a left repeat bar") +delete s.text} +curvoice.tie_s_rep=null} +if(s.text){s.rbstart=s.rbstop=2 +if(s.text[0]=='1'){curvoice.tie_s_rep=curvoice.tie_s +if(curvoice.acc_tie) +curvoice.acc_tie_rep=curvoice.acc_tie.slice() +else if(curvoice.acc_tie_rep) +curvoice.acc_tie_rep=null}else{curvoice.tie_s=curvoice.tie_s_rep +if(curvoice.acc_tie_rep) +curvoice.acc_tie=curvoice.acc_tie_rep.slice()} +if(curvoice.norepbra&&!curvoice.second) +s.norepbra=1} +if(curvoice.ulen<0) +adjust_dur(s);if((bar_type=="["||bar_type=="|:")&&!curvoice.eoln&&!s.a_gch&&!s.invis){s2=curvoice.last_sym +if(s2&&s2.type==C.BAR){if((bar_type=="["&&!s2.text)||s.norepbra){if(s.text){s2.text=s.text +if(curvoice.st&&!s.norepbra&&!(par_sy.staves[curvoice.st-1].flags&STOP_BAR)) +s2.xsh=4} +if(s.norepbra) +s2.norepbra=1 +if(s.rbstart) +s2.rbstart=s.rbstart +if(s.rbstop) +s2.rbstop=s.rbstop +return} +if(bar_type=="|:"){switch(s2.bar_type){case":|":s2.bar_type="::";s2.rbstop=2 +return}}}} +switch(bar_type){case"[":case"[]":case"[|]":s.invis=true;bar_type=s.rbstart?"[":"[]" +break +case":|:":case":||:":bar_type="::" +break +case"||":if(cfmt["abc-version"]>="2.2") +break +case"[|":case"|]":s.rbstop=2 +break} +s.bar_type=bar_type +if(!curvoice.lyric_restart) +curvoice.lyric_restart=s +if(!curvoice.sym_restart) +curvoice.sym_restart=s +sym_link(s);s.st=curvoice.st +if(s.text&&s.st>0&&!s.norepbra&&!(par_sy.staves[s.st-1].flags&STOP_BAR)&&bar_type!='[') +s.xsh=4 +if(!s.bar_dotted&&!s.invis) +curvoice.acc=[]} +function parse_staves(p){var v,vid,vids={},a_vf=[],err=false,flags=0,brace=0,bracket=0,parenth=0,flags_st=0,e,a=p.match(/[^[\]|{}()*+\s]+|[^\s]/g) +if(!a){syntax(1,errs.bad_val,"%%score") +return} +while(1){e=a.shift() +if(!e) +break +switch(e){case'[':if(parenth||brace+bracket>=2){syntax(1,errs.misplaced,'[');err=true +break} +flags|=brace+bracket==0?OPEN_BRACKET:OPEN_BRACKET2;bracket++;flags_st<<=8;flags_st|=OPEN_BRACKET +break +case'{':if(parenth||brace||bracket>=2){syntax(1,errs.misplaced,'{');err=true +break} +flags|=!bracket?OPEN_BRACE:OPEN_BRACE2;brace++;flags_st<<=8;flags_st|=OPEN_BRACE +break +case'(':if(parenth){syntax(1,errs.misplaced,'(');err=true +break} +flags|=OPEN_PARENTH;parenth++;flags_st<<=8;flags_st|=OPEN_PARENTH +break +case'*':if(brace&&!parenth&&!(flags&(OPEN_BRACE|OPEN_BRACE2))) +flags|=FL_VOICE +break +case'+':flags|=MASTER_VOICE +break +case']':case'}':case')':syntax(1,"Bad voice ID in %%score");err=true +break +default:vid=e +while(1){e=a.shift() +if(!e) +break +switch(e){case']':if(!(flags_st&OPEN_BRACKET)){syntax(1,errs.misplaced,']');err=true +break} +bracket--;flags|=brace+bracket==0?CLOSE_BRACKET:CLOSE_BRACKET2;flags_st>>=8 +continue +case'}':if(!(flags_st&OPEN_BRACE)){syntax(1,errs.misplaced,'}');err=true +break} +brace--;flags|=!bracket?CLOSE_BRACE:CLOSE_BRACE2;flags&=~FL_VOICE;flags_st>>=8 +continue +case')':if(!(flags_st&OPEN_PARENTH)){syntax(1,errs.misplaced,')');err=true +break} +parenth--;flags|=CLOSE_PARENTH;flags_st>>=8 +continue +case'|':flags|=STOP_BAR +continue} +break} +if(vids[vid]){syntax(1,"Double voice in %%score") +err=true}else{vids[vid]=true +a_vf.push([vid,flags])} +flags=0 +if(!e) +break +a.unshift(e) +break}} +if(flags_st!=0){syntax(1,"'}', ')' or ']' missing in %%score");err=true} +if(err||!a_vf.length) +return +return a_vf} +function info_split(text){if(!text) +return[] +var a=text.match(/[^\s"=]+=?|"[^"]*"/g) +if(!a){syntax(1,"Unterminated string") +return[]} +return a} +var reg_dur=/(\d*)(\/*)(\d*)/g +function parse_dur(line){var res,num,den;reg_dur.lastIndex=line.index;res=reg_dur.exec(line.buffer) +if(!res[0]) +return[1,1];num=res[1]||1;den=res[3]||1 +if(!res[3]) +den*=1<='1'&&c<='9')||c=='/'){nd=parse_dur(line);if(acc<0) +nd[0]=-nd[0] +if(cfmt.nedo&&nd[1]==1){nd[0]*=12 +nd[1]*=cfmt.nedo} +acc=nd +c=line.char()}} +pit=ntb.indexOf(c)+16;c=line.next_char() +if(pit<16){syntax(1,"'$1' is not a note",line.buffer[line.index-1]) +return} +while(c=="'"){pit+=7;c=line.next_char()} +while(c==','){pit-=7;c=line.next_char()} +note={pit:pit,shhd:0,shac:0} +if(acc) +note.acc=acc +return note} +function set_map(note,acc){var pit=note.pit,nn=not2abc(pit,acc),map=maps[curvoice.map] +if(!map[nn]){nn='o'+nn.replace(/[',]+/,'') +if(!map[nn]){nn='k'+ntb[(pit+75- +curvoice.ckey.k_sf*11)%7] +if(!map[nn]){nn='all' +if(!map[nn]) +return}}} +note.map=map=map[nn] +if(map[1]){note.pit=pit=map[1].pit +note.acc=map[1].acc +if(map[1].notrp){note.notrp=1 +note.noplay=1}} +if(map[2]) +note.color=map[2] +nn=map[3] +if(nn) +note.midi=pit2mid(nn.pit+19,nn.acc)} +function parse_basic_note(line,ulen){var nd,note=parse_acc_pit(line) +if(!note) +return +if(line.char()=='0'){parse.stemless=true;line.index++} +nd=parse_dur(line);note.dur=ulen*nd[0]/nd[1] +return note} +function parse_vpos(){var line=parse.line,ty=0 +if(a_dcn.length&&a_dcn[a_dcn.length-1]=="dot"){ty=C.SL_DOTTED +a_dcn.pop()} +switch(line.next_char()){case"'":line.index++ +return ty+C.SL_ABOVE +case",":line.index++ +return ty+C.SL_BELOW +case'?':line.index++ +return ty+C.SL_CENTER} +return ty+C.SL_AUTO} +function slur_add(s,nt){var i,s2,sl +for(i=curvoice.sls.length;--i>=0;){sl=curvoice.sls[i] +if(sl.ss==s) +continue +curvoice.sls.splice(i,1) +sl.se=s +if(nt) +sl.nte=nt +s2=sl.ss +if(!s2.sls) +s2.sls=[] +s2.sls.push(sl) +if(sl.grace) +sl.grace.sl1=true +return} +for(s2=s.prev;s2;s2=s2.prev){if(s2.type==C.BAR&&s2.bar_type[0]==':'&&s2.text){if(!s2.sls) +s2.sls=[];s2.sls.push({ty:C.SL_AUTO,ss:s2,se:s}) +if(nt) +s2.sls[s2.sls.length-1].nte=nt +return}} +if(!s.sls) +s.sls=[];s.sls.push({ty:C.SL_AUTO,se:s,loc:'i'}) +if(nt) +s.sls[s.sls.length-1].nte=nt} +function pit2mid(pit,acc){var p=[0,2,4,5,7,9,11][pit%7],o=((pit/7)|0)*12,p0,p1,s,b40 +if(curvoice.snd_oct) +o+=curvoice.snd_oct +if(acc==3) +acc=0 +if(acc){if(typeof acc=="object"){s=acc[0]/acc[1] +if(acc[1]==100) +return p+o+s}else{s=acc}}else{if(cfmt.temper) +return cfmt.temper[abc2svg.p_b40[pit%7]]+o +return p+o} +if(!cfmt.nedo){if(!cfmt.temper){p+=o+s +return p}}else{if(typeof acc!="object"){b40=abc2svg.p_b40[pit%7]+acc +return cfmt.temper[b40]+o} +if(acc[1]==cfmt.nedo){b40=abc2svg.p_b40[pit%7] +return cfmt.temper[b40]+o+s}} +p0=cfmt.temper[abc2svg.p_b40[pit%7]] +if(s>0){p1=cfmt.temper[(abc2svg.p_b40[pit%7]+1)%40] +if(p1p0) +p1-=12 +s=-s} +return p0+o+(p1-p0)*s} +function do_ties(s,tie_s){var i,m,not1,not2,mid,g,nt=0,se=(tie_s.time+tie_s.dur)==curvoice.time +for(m=0;m<=s.nhd;m++){not2=s.notes[m] +mid=not2.midi +if(tie_s.type!=C.GRACE){for(i=0;i<=tie_s.nhd;i++){not1=tie_s.notes[i] +if(!not1.tie_ty) +continue +if(not1.midi==mid&&(!se||!not1.tie_e)){not2.tie_s=not1 +not2.s=s +if(se){not1.tie_e=not2 +not1.s=tie_s} +nt++ +break}}}else{for(g=tie_s.extra;g;g=g.next){not1=g.notes[0] +if(!not1.tie_ty) +continue +if(not1.midi==mid){g.ti1=true +not2.tie_s=not1 +not2.s=s +not1.tie_e=not2 +not1.s=g +nt++ +break}}}} +if(!nt) +error(1,tie_s,"Bad tie") +else +s.ti2=true} +Abc.prototype.new_note=function(grace,sls){var note,s,in_chord,c,dcn,type,tie_s,acc_tie,i,n,s2,nd,res,num,dur,apit,div,ty,dpit=0,sl1=[],line=parse.line,a_dcn_sav=a_dcn +a_dcn=[] +parse.stemless=false;s={type:C.NOTE,fname:parse.fname,stem:0,multi:0,nhd:0,xmx:0} +s.istart=parse.bol+line.index +if(curvoice.color) +s.color=curvoice.color +if(grace){s.grace=true}else{if(curvoice.tie_s){tie_s=curvoice.tie_s +curvoice.tie_s=null} +if(a_gch) +csan_add(s) +if(parse.repeat_n){s.repeat_n=parse.repeat_n;s.repeat_k=parse.repeat_k;parse.repeat_n=0}} +c=line.char() +switch(c){case'X':s.invis=true +case'Z':s.type=C.MREST;c=line.next_char() +s.nmes=(c>'0'&&c<='9')?line.get_int():1;if(curvoice.wmeasure==1){error(1,s,"multi-measure rest, but no measure!") +return} +s.dur=curvoice.wmeasure*s.nmes +if(curvoice.second){delete curvoice.eoln +curvoice.time+=s.dur +return} +if(s.nmes==1){s.type=C.REST;s.dur_orig=s.dur;s.fmr=1 +s.notes=[{pit:18,dur:s.dur}]}else{glovar.mrest_p=true +if(par_sy.voices.length==1){s.tacet=curvoice.tacet +delete s.invis}} +break +case'y':s.type=C.SPACE;s.invis=true;s.dur=0;c=line.next_char() +if(c>='0'&&c<='9') +s.width=line.get_int() +else +s.width=10 +if(tie_s){curvoice.tie_s=tie_s +tie_s=null} +break +case'x':s.invis=true +case'z':s.type=C.REST;line.index++;nd=parse_dur(line);s.dur_orig=((curvoice.ulen<0)?C.BLEN:curvoice.ulen)*nd[0]/nd[1];s.dur=s.dur_orig*curvoice.dur_fact;if(s.dur==curvoice.wmeasure) +s.fmr=1 +s.notes=[{pit:18,dur:s.dur_orig}] +break +case'[':in_chord=true;c=line.next_char() +default:if(curvoice.acc_tie){acc_tie=curvoice.acc_tie +curvoice.acc_tie=null} +s.notes=[] +while(1){if(in_chord){while(1){if(!c) +break +i=c.charCodeAt(0);if(i>=128){syntax(1,errs.not_ascii) +return} +type=char_tb[i] +switch(type[0]){case'(':sl1.push(parse_vpos());c=line.char() +continue +case'!':if(type.length>1) +a_dcn.push(type.slice(1,-1)) +else +get_deco() +c=line.next_char() +continue} +break}} +note=parse_basic_note(line,s.grace?C.BLEN/4:curvoice.ulen<0?C.BLEN:curvoice.ulen) +if(!note) +return +if(curvoice.octave) +note.pit+=curvoice.octave*7 +apit=note.pit+19 +i=note.acc +if(!i){if(cfmt["propagate-accidentals"][0]=='p') +i=curvoice.acc[apit%7] +else +i=curvoice.acc[apit] +if(!i) +i=curvoice.ckey.k_map[apit%7]||0} +if(i){if(cfmt["propagate-accidentals"][0]=='p') +curvoice.acc[apit%7]=i +else if(cfmt["propagate-accidentals"][0]!='n') +curvoice.acc[apit]=i} +if(acc_tie&&acc_tie[apit]) +i=acc_tie[apit] +if(curvoice.map&&maps[curvoice.map]) +set_map(note,i) +if(!note.midi) +note.midi=pit2mid(apit,i) +if(curvoice.tr_sco&&!note.notrp){i=nt_trans(note,i) +if(i==-3){error(1,s,"triple sharp/flat") +i=note.acc>0?1:-1 +note.pit+=i +note.acc=i} +dpit=note.pit+19-apit} +if(curvoice.tr_snd) +note.midi+=curvoice.tr_snd +if(i){switch(cfmt["writeout-accidentals"][1]){case'd':s2=curvoice.ckey +if(!s2.k_a_acc) +break +for(n=0;n0){n=num*2-1;s.dur=s.dur*n/num;s.dur_orig=s.dur_orig*n/num +for(i=0;i<=s.nhd;i++) +s.notes[i].dur=s.notes[i].dur*n/num;s2.dur/=num;s2.dur_orig/=num +for(i=0;i<=s2.nhd;i++) +s2.notes[i].dur/=num}else{num=-num;n=num*2-1;s.dur/=num;s.dur_orig/=num +for(i=0;i<=s.nhd;i++) +s.notes[i].dur/=num;s2.dur=s2.dur*n/num;s2.dur_orig=s2.dur_orig*n/num +for(i=0;i<=s2.nhd;i++) +s2.notes[i].dur=s2.notes[i].dur*n/num} +curvoice.time=s2.time+s2.dur;for(s2=s2.next;s2;s2=s2.next) +s2.time=curvoice.time}}else{div=curvoice.ckey.k_bagpipe?8:4 +for(i=0;i<=s.nhd;i++) +s.notes[i].dur/=div;s.dur/=div;s.dur_orig/=div +if(grace.stem) +s.stem=grace.stem} +curvoice.last_note=s +c=line.char() +while(1){switch(c){case'.':if(line.buffer[line.index+1]!='-') +break +a_dcn.push("dot") +line.index++ +case'-':ty=parse_vpos() +for(i=0;i<=s.nhd;i++){s.notes[i].tie_ty=ty +s.notes[i].s=s} +curvoice.tie_s=grace||s +curvoice.tie_s.ti1=true +for(i=0;i<=s.nhd;i++){note=s.notes[i] +apit=note.pit+19 +-dpit +if(curvoice.acc[apit]||(acc_tie&&acc_tie[apit])){if(!curvoice.acc_tie) +curvoice.acc_tie=[] +n=curvoice.acc[apit] +if(acc_tie&&acc_tie[apit]) +n=acc_tie[apit] +curvoice.acc_tie[apit]=n}} +c=line.char() +continue} +break} +if(tie_s) +do_ties(s,tie_s)} +sym_link(s) +if(!grace){if(!curvoice.lyric_restart) +curvoice.lyric_restart=s +if(!curvoice.sym_restart) +curvoice.sym_restart=s} +if(a_dcn_sav.length){a_dcn=a_dcn_sav +deco_cnv(s,s.prev)} +if(grace&&s.ottava) +grace.ottava=s.ottava +if(parse.stemless) +s.stemless=true +s.iend=parse.bol+line.index +return s} +function tp_adj(s,fact){var d,tim=s.time,to=curvoice.time-tim,tt=to*fact +curvoice.time=tim+tt +while(1){s.in_tuplet=true +if(!s.grace){s.time=tim +if(s.dur){d=Math.round(s.dur*tt/to) +to-=s.dur +s.dur=d +tt-=s.dur +tim+=s.dur}} +if(!s.next){if(s.tpe) +s.tpe++ +else +s.tpe=1 +break} +s=s.next}} +function get_deco(){var c,line=parse.line,i=line.index,dcn="" +while(1){c=line.next_char() +if(!c){line.index=i +syntax(1,"No end of decoration") +return} +if(c=='!') +break +dcn+=c} +a_dcn.push(dcn)} +var nil="0",char_tb=[nil,nil,nil,nil,nil,nil,nil,nil,nil," ","\n",nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil," ","!",'"',"i","\n",nil,"&",nil,"(",")","i",nil,nil,"-","!dot!",nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,"|","i","<","n","<","i","i","n","n","n","n","n","n","n","!fermata!","d","d","d","!emphasis!","!lowermordent!","d","!coda!","!uppermordent!","d","d","!segno!","!trill!","d","d","d","n","d","n","[","\\","|","n","n","i","n","n","n","n","n","n","n","d","d","d","d","d","d","d","d","d","d","d","d","d","!upbow!","!downbow!","d","n","n","n","{","|","}","!gmark!",nil,] +function parse_music_line(){var grace,last_note_sav,a_dcn_sav,no_eol,s,tps,tp=[],tpn=-1,sls=[],line=parse.line +function check_mac(m){var i,j,b +for(i=1,j=line.index+1;i=14){n-=7;c+="'"} +return ntb[n]+c} +function expand(m,b){if(b==undefined) +return m +var c,i,r="",n=m.length +for(i=0;i='h'&&c<='z'){r+=n2n(b+c.charCodeAt(0)-'n'.charCodeAt(0))}else{r+=c}} +return r} +function parse_mac(k,m,b){var te,ti,curv,s,line_sav=line,istart_sav=parse.istart;parse.line=line=new scanBuf;parse.istart+=line_sav.index;if(cfmt.writefields.indexOf('m')<0){line.buffer=k.replace('n',n2n(b)) +s=curvoice.last_sym +ti=curvoice.time +parse_seq(true) +if(!s) +s=curvoice.sym +for(s=s.next;s;s=s.next) +s.noplay=true +te=curvoice.time +curv=curvoice +curvoice=clone_voice(curv.id+'-p') +if(!par_sy.voices[curvoice.v]){curvoice.second=true +par_sy.voices[curvoice.v]={st:curv.st,second:true,range:curvoice.v}} +curvoice.time=ti +s=curvoice.last_sym +parse.line=line=new scanBuf +parse.istart+=line_sav.index +line.buffer=expand(m,b) +parse_seq(true) +if(curvoice.time!=te) +syntax(1,"Bad length of the macro sequence") +if(!s) +s=curvoice.sym +for(;s;s=s.next) +s.invis=s.play=true +curvoice=curv}else{line.buffer=expand(m,b) +parse_seq(true)} +parse.line=line=line_sav +parse.istart=istart_sav} +function parse_seq(in_mac){var c,idx,type,k,s,dcn,i,n,text,note +while(1){c=line.char() +if(!c) +break +if(!in_mac&&maci[c]){n=undefined +for(k in mac){if(!mac.hasOwnProperty(k)||k[0]!=c) +continue +if(k.indexOf('n')<0){if(line.buffer.indexOf(k,line.index) +!=line.index) +continue +line.index+=k.length}else{n=check_mac(k) +if(n==undefined) +continue} +parse_mac(k,mac[k],n) +n=1 +break} +if(n) +continue} +idx=c.charCodeAt(0) +if(idx>=128){syntax(1,errs.not_ascii) +line.index++ +break} +type=char_tb[idx] +switch(type[0]){case' ':s=curvoice.last_note +if(s){s.beam_end=true +if(grace) +grace.gr_shift=true} +break +case'\n':if(cfmt.barsperstaff) +break +curvoice.eoln=true +break +case'&':if(grace){syntax(1,errs.bad_grace) +break} +c=line.next_char() +if(c==')'){get_vover(c) +break} +get_vover('&') +continue +case'(':c=line.next_char() +if(c>'0'&&c<='9'){if(grace){syntax(1,errs.bad_grace) +break} +var pplet=line.get_int(),qplet=qplet_tb[pplet],rplet=pplet +c=line.char() +if(c==':'){c=line.next_char() +if(c>'0'&&c<='9'){qplet=line.get_int();c=line.char()} +if(c==':'){c=line.next_char() +if(c>'0'&&c<='9'){rplet=line.get_int();c=line.char()}else{syntax(1,"Invalid 'r' in tuplet") +continue}}} +if(qplet==0||qplet==undefined) +qplet=(curvoice.wmeasure%9)==0?3:2;if(tpn<0) +tpn=tp.length +tp.push({p:pplet,q:qplet,r:rplet,ro:rplet,f:curvoice.tup||cfmt.tuplets}) +continue} +if(c=='&'){if(grace){syntax(1,errs.bad_grace) +break} +get_vover('(') +break} +line.index--;sls.push(parse_vpos()) +continue +case')':s=curvoice.last_sym +if(s){switch(s.type){case C.SPACE:if(!s.notes){s.notes=[] +s.notes[0]={}} +case C.NOTE:case C.REST:break +case C.GRACE:for(s=s.extra;s.next;s=s.next);break +default:s=null +break}} +if(!s){syntax(1,errs.bad_char,c) +break} +slur_add(s) +break +case'!':if(type.length>1) +a_dcn.push(type.slice(1,-1)) +else +get_deco() +break +case'"':if(grace){syntax(1,errs.bad_grace) +break} +parse_gchord(type) +break +case'[':if(type.length>1){self.do_pscom(type.slice(3,-1)) +break} +var c_next=line.buffer[line.index+1] +if('|[]: "'.indexOf(c_next)>=0||(c_next>='1'&&c_next<='9')){if(grace){syntax(1,errs.bar_grace) +break} +new_bar() +continue} +if(line.buffer[line.index+2]==':'){if(grace){syntax(1,errs.bad_grace) +break} +i=line.buffer.indexOf(']',line.index+1) +if(i<0){syntax(1,"Lack of ']'") +break} +text=line.buffer.slice(line.index+3,i).trim() +parse.istart=parse.bol+line.index;parse.iend=parse.bol+ ++i;line.index=0;do_info(c_next,text);line.index=i +continue} +case'n':s=self.new_note(grace,sls) +if(!s) +continue +if(grace||!s.notes) +continue +if(tpn>=0){s.tp=tp.slice(tpn) +tpn=-1 +if(tps) +s.tp[0].s=tps +tps=s}else if(!tps){continue} +k=tp[tp.length-1] +if(--k.r>0) +continue +while(1){tp_adj(tps,k.q/k.p) +i=k.ro +if(k.s) +tps=k.s +tp.pop() +if(!tp.length){tps=null +break} +k=tp[tp.length-1] +k.r-=i +if(k.r>0) +break} +continue +case'<':if(!curvoice.last_note){syntax(1,"No note before '<'") +break} +if(grace){syntax(1,"Cannot have a broken rhythm in grace notes") +break} +n=c=='<'?1:-1 +while(c=='<'||c=='>'){n*=2;c=line.next_char()} +curvoice.brk_rhythm=n +continue +case'i':break +case'{':if(grace){syntax(1,"'{' in grace note") +break} +last_note_sav=curvoice.last_note;curvoice.last_note=null;a_dcn_sav=a_dcn;a_dcn=[] +grace={type:C.GRACE,fname:parse.fname,istart:parse.bol+line.index,dur:0,multi:0} +if(curvoice.color) +grace.color=curvoice.color +switch(curvoice.pos.gst&0x07){case C.SL_ABOVE:grace.stem=1;break +case C.SL_BELOW:grace.stem=-1;break +case C.SL_HIDDEN:grace.stem=2;break} +sym_link(grace);c=line.next_char() +if(c=='/'){grace.sappo=true +break} +continue +case'|':if(grace){syntax(1,errs.bar_grace) +break} +new_bar() +continue +case'}':if(curvoice.ignore){grace=null +break} +s=curvoice.last_note +if(!grace||!s){syntax(1,errs.bad_char,c) +break} +if(a_dcn.length) +syntax(1,"Decoration ignored");grace.extra=grace.next;grace.extra.prev=null;grace.next=null;curvoice.last_sym=grace;grace=null +if(!s.prev&&!curvoice.ckey.k_bagpipe){for(i=0;i<=s.nhd;i++) +s.notes[i].dur*=2;s.dur*=2;s.dur_orig*=2} +curvoice.last_note=last_note_sav;a_dcn=a_dcn_sav +break +case"\\":if(!line.buffer[line.index+1]){no_eol=true +break} +default:syntax(1,errs.bad_char,c) +break} +line.index++}} +if(parse.state!=3) +return +if(parse.tp){tp=parse.tp +tpn=parse.tpn +tps=parse.tps +parse.tp=null} +parse_seq() +if(tp.length){parse.tp=tp +parse.tps=tps +parse.tpn=tpn} +if(sls.length) +syntax(1,"Start of slur without note") +if(grace){syntax(1,"No end of grace note sequence");curvoice.last_sym=grace.prev;curvoice.last_note=last_note_sav +if(grace.prev) +grace.prev.next=null} +if(!no_eol&&!cfmt.barsperstaff&&!vover&&char_tb['\n'.charCodeAt(0)]=='\n') +curvoice.eoln=true +if(curvoice.eoln&&cfmt.breakoneoln&&curvoice.last_note) +curvoice.last_note.beam_end=true} +var sheet +var add_fstyle=typeof document!="undefined"?function(s){var e +if(cfmt.fullsvg) +font_style+="\n"+s +if(!sheet){if(abc2svg.sheet){sheet=abc2svg.sheet +e=sheet.cssRules.length +while(--e>=0) +sheet.deleteRule(e)}else{e=document.createElement('style') +document.head.appendChild(e) +abc2svg.sheet=sheet=e.sheet}} +s=s.match(/[^{]+{[^}]+}/g) +while(1){e=s.shift() +if(!e) +break +sheet.insertRule(e,sheet.cssRules.length)}}:function(s){font_style+="\n"+s} +var +sw_tb=new Float32Array([.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.250,.333,.408,.500,.500,.833,.778,.333,.333,.333,.500,.564,.250,.564,.250,.278,.500,.500,.500,.500,.500,.500,.500,.500,.500,.500,.278,.278,.564,.564,.564,.444,.921,.722,.667,.667,.722,.611,.556,.722,.722,.333,.389,.722,.611,.889,.722,.722,.556,.722,.667,.556,.611,.722,.722,.944,.722,.722,.611,.333,.278,.333,.469,.500,.333,.444,.500,.444,.500,.444,.333,.500,.500,.278,.278,.500,.278,.778,.500,.500,.500,.500,.333,.389,.278,.500,.500,.722,.500,.500,.444,.480,.200,.480,.541,.500]),ssw_tb=new Float32Array([.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.278,.278,.355,.556,.556,.889,.667,.191,.333,.333,.389,.584,.278,.333,.278,.278,.556,.556,.556,.556,.556,.556,.556,.556,.556,.556,.278,.278,.584,.584,.584,.556,1.015,.667,.667,.722,.722,.667,.611,.778,.722,.278,.500,.667,.556,.833,.722,.778,.667,.778,.722,.667,.611,.722,.667,.944,.667,.667,.611,.278,.278,.278,.469,.556,.333,.556,.556,.500,.556,.556,.278,.556,.556,.222,.222,.500,.222,.833,.556,.556,.556,.556,.333,.500,.278,.556,.500,.722,.500,.500,.500,.334,.260,.334,.584,.512]),mw_tb=new Float32Array([.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52]) +function cwid(c,font){var i=c.charCodeAt(0) +if(i>=0x80){if(i>=0x300&&i<0x370) +return 0;i=0x61} +return(font||gene.curfont).cw_tb[i]} +function cwidf(c){return cwid(c)*gene.curfont.swfac} +var strwh +(function(){if(typeof document!="undefined"){var el +strwh=function(str){if(str.wh) +return str.wh +if(!el){el=document.createElement('text') +el.style.position='absolute' +el.style.top='-1000px' +el.style.padding='0' +el.style.visibility="hidden" +document.body.appendChild(el)} +var c,font=gene.curfont,h=font.size,w=0,n=str.length,i0=0,i=0 +el.className=font_class(font) +el.style.lineHeight=1 +if(typeof str=="object"){el.innerHTML=str +str.wh=[el.clientWidth,el.clientHeight] +return str.wh} +str=str.replace(/<|>|&[^&\s]*?;|&/g,function(c){switch(c){case'<':return"<" +case'>':return">" +case'&':return"&"} +return c}) +while(1){i=str.indexOf('$',i) +if(i>=0){c=str[i+1] +if(c=='0'){font=gene.deffont}else if(c>='1'&&c<='9'){font=get_font("u"+c)}else{i++ +continue}} +el.innerHTML=str.slice(i0,i>=0?i:undefined) +w+=el.clientWidth +if(el.clientHeight>h) +h=el.clientHeight +if(i<0) +break +el.style.font=style_font(font).slice(5);i+=2;i0=i} +return[w,h]}}else{strwh=function(str){var font=gene.curfont,swfac=font.swfac,h=font.size,w=0,i,j,c,n=str.length +for(i=0;i='1'&&c<='9'){font=get_font("u"+c)}else{c='$' +break} +i++;swfac=font.swfac +if(font.size>h) +h=font.size +continue +case'&':if(str[i+1]==' ') +break +j=str.indexOf(';',i) +if(j>0&&j-i<10){i=j;c='a'} +break} +w+=cwid(c,font)*swfac} +return[w,h]}}})() +function str2svg(str){if(typeof str=="object") +return str +var n_font,wh,o_font=gene.deffont,c_font=gene.curfont,o="" +function tspan(nf,of){var cl +if(nf.class&&nf.name==of.name&&nf.size==of.size&&nf.weight==of.weight&&nf.style==of.style) +cl=nf.class +else +cl=font_class(nf) +return''} +if(c_font!=o_font) +o=tspan(c_font,o_font) +o+=str.replace(/<|>|&[^&\s]*?;|&|\$./g,function(c){switch(c){case'<':return"<" +case'>':return">" +case'&':return"&" +default:if(c[0]!='$') +break +if(c[1]=='0') +n_font=gene.deffont +else if(c[1]>='1'&&c[1]<='9') +n_font=get_font("u"+c[1]) +else +break +c='' +if(n_font==c_font) +return c +if(c_font!=o_font) +c="" +c_font=n_font +if(c_font==o_font) +return c +return c+tspan(c_font,o_font)} +return c}) +if(c_font!=o_font) +o+="" +o=new String(o) +if(typeof document!="undefined") +strwh(o) +else +o.wh=strwh(str) +gene.curfont=c_font +return o} +function set_font(xxx){if(typeof xxx=="string") +xxx=get_font(xxx) +gene.curfont=gene.deffont=xxx} +function out_str(str){output+=str2svg(str)} +function xy_str(x,y,str,action,w,wh){if(!wh) +wh=str.wh||strwh(str) +if(cfmt.trimsvg){var wx=wh[0] +switch(action){case'c':wx=wh[0]/2 +break +case'j':wx=w +break +case'r':wx=0 +break} +if(img.wx5&&gene.deffont.wadj) +output+='" lengthAdjust="'+gene.deffont.wadj+'" textLength="'+wh[0].toFixed(1);output+='" x="';out_sxsy(x,'" y="',y) +switch(action){case'c':output+='" text-anchor="middle">' +break +case'j':output+='" textLength="'+w.toFixed(1)+'">' +break +case'r':output+='" text-anchor="end">' +break +default:output+='">' +break} +out_str(str);output+="\n"} +function trim_title(title,is_subtitle){var i +if(cfmt.titletrim){i=title.lastIndexOf(", ") +if(i<0||title[i+2]<'A'||title[i+2]>'Z'){i=0}else if(cfmt.titletrim==1){if(i=0) +i=0}else{if(i=0) +title=info.X+'. '+title +if(cfmt.titlecaps) +return title.toUpperCase() +return title} +function get_lwidth(){if(img.chg) +set_page() +return(img.width-img.lm-img.rm +-2) +/ cfmt.scale} +function write_title(title,is_subtitle){var h,wh +if(!title) +return +set_page();title=trim_title(title,is_subtitle) +if(is_subtitle){set_font("subtitle");h=cfmt.subtitlespace}else{set_font("title");h=cfmt.titlespace} +wh=strwh(title) +wh[1]+=gene.curfont.pad*2 +vskip(wh[1]+h+gene.curfont.pad) +h=gene.curfont.pad+wh[1]*.22 +if(cfmt.titleleft) +xy_str(0,h,title,null,null,wh) +else +xy_str(get_lwidth()/2,h,title,"c",null,wh)} +function put_inf2r(x,y,str1,str2,action){if(!str1){if(!str2) +return +str1=str2;str2=null} +if(!str2) +xy_str(x,y,str1,action) +else +xy_str(x,y,str1+' ('+str2+')',action)} +function write_text(text,action){if(action=='s') +return +set_page();var wh,font,o,strlw=get_lwidth(),sz=gene.curfont.size,lineskip=sz*cfmt.lineskipfac,parskip=sz*cfmt.parskipfac,i,j,x,words,w,k,ww,str;switch(action){default:font=gene.curfont +switch(action){case'c':x=strlw/2;break +case'r':x=strlw-font.pad;break +default:x=font.pad;break} +j=0 +while(1){i=text.indexOf('\n',j) +if(i==j){vskip(parskip);blk_flush() +use_font(gene.curfont) +while(text[i+1]=='\n'){vskip(lineskip);i++} +if(i==text.length) +break}else{if(i<0) +str=text.slice(j) +else +str=text.slice(j,i) +ww=strwh(str) +vskip(ww[1]*cfmt.lineskipfac ++font.pad*2) +xy_str(x,font.pad+ww[1]*.2,str,action) +if(i<0) +break} +j=i+1} +vskip(parskip);blk_flush() +break +case'f':case'j':j=0 +while(1){i=text.indexOf('\n\n',j) +if(i<0) +words=text.slice(j) +else +words=text.slice(j,i);words=words.split(/\s+/);w=k=wh=0 +for(j=0;j=strlw){vskip(wh*cfmt.lineskipfac) +xy_str(0,ww[1]*.2,words.slice(k,j).join(' '),action,strlw) +k=j;w=ww[0] +wh=0} +if(ww[1]>wh) +wh=ww[1]} +if(w!=0){vskip(wh*cfmt.lineskipfac) +xy_str(0,ww[1]*.2,words.slice(k).join(' '))} +vskip(parskip);blk_flush() +if(i<0) +break +while(text[i+2]=='\n'){vskip(lineskip);i++} +if(i==text.length) +break +use_font(gene.curfont);j=i+2} +break}} +function put_words(words){var p,i,j,nw,w,lw,x1,x2,i1,i2,do_flush,maxn=0,n=1 +function put_wline(p,x){var i=0,k=0 +if(p[0]=='$'&&p[1]>='0'&&p[1]<='9'){gene.curfont=p[1]=='0'?gene.deffont:get_font("u"+p[1]) +p=p.slice(2)} +if((p[i]>='0'&&p[i]<='9')||p[i+1]=='.'){while(imaxn){maxn=p.length +i1=i}} +w=get_lwidth()/2 +lw=strwh(words[i1])[0] +i1=i2=0 +if(lw>1 +for(i=0;i>=1} +if(i2){x1=(w-lw)/2+10 +x2=x1+w}else{x2=w-lw/2+10} +do_flush=true +for(i=0;i'Z'){switch(c){case'+':align='+' +c=p[i++] +break +case',':fmt+='\n' +default:continue +case'<':align='l' +c=p[i++] +break +case'>':align='r' +c=p[i++] +break}}else{switch(p[i]){case'-':align='l' +i++ +break +case'1':align='r' +i++ +break +case'0':i++ +default:align='c' +break}} +if(!info_val[c]){if(!info[c]) +continue +info_val[c]=info[c].split('\n');info_nb[c]=1}else{info_nb[c]++} +fmt+=align+c} +fmt+='\n' +var ya={l:cfmt.titlespace,c:cfmt.titlespace,r:cfmt.titlespace},xa={l:0,c:lwidth*.5,r:lwidth},yb={},str;p=fmt;i=0 +while(1){yb.l=yb.c=yb.r=y=0;j=i +while(1){align=p[j++] +if(align=='\n') +break +c=p[j++] +if(align=='+'||yb[align]) +continue +str=info_val[c] +if(!str) +continue +font_name=info_font[c] +if(!font_name) +font_name="history";font=get_font(font_name);sz=font.size*1.1 +if(info_sz[c]) +sz+=info_sz[c] +if(y0){y+=sz;str=info_val[c].shift();xy_str(x,-y,str,align)}} +info_nb[c]--;ya[align]=y} +if(ya.c>ya.l) +ya.l=ya.c +if(ya.r>ya.l) +ya.l=ya.r +if(i>=p.length) +break +ya.c=ya.r=ya.l} +vskip(ya.l)} +function write_heading(){var i,j,area,composer,origin,rhythm,down1,down2,lwidth=get_lwidth() +vskip(cfmt.topspace) +if(cfmt.titleformat){write_headform(lwidth);vskip(cfmt.musicspace) +return} +if(info.T&&cfmt.writefields.indexOf('T')>=0){i=0 +while(1){j=info.T.indexOf("\n",i) +if(j<0){write_title(info.T.substring(i),i!=0) +break} +write_title(info.T.slice(i,j),i!=0);i=j+1}} +down1=down2=0 +if(parse.ckey.k_bagpipe&&!cfmt.infoline&&cfmt.writefields.indexOf('R')>=0) +rhythm=info.R +if(rhythm){set_font("composer");down1=cfmt.composerspace+gene.curfont.size+2 +xy_str(0,-down1+gene.curfont.size*.22,rhythm)} +area=info.A +if(cfmt.writefields.indexOf('C')>=0) +composer=info.C +if(cfmt.writefields.indexOf('O')>=0) +origin=info.O +if(composer||origin||cfmt.infoline){var xcomp,align;set_font("composer");if(cfmt.aligncomposer<0){xcomp=0;align=' '}else if(cfmt.aligncomposer==0){xcomp=lwidth*.5;align='c'}else{xcomp=lwidth;align='r'} +if(composer||origin){down2=cfmt.composerspace+2 +i=0 +while(1){down2+=gene.curfont.size +if(composer) +j=composer.indexOf("\n",i) +else +j=-1 +if(j<0){put_inf2r(xcomp,-down2+gene.curfont.size*.22,composer?composer.substring(i):null,origin,align) +break} +xy_str(xcomp,-down2+gene.curfont.size*.22,composer.slice(i,j),align);i=j+1}} +rhythm=rhythm?null:info.R +if((rhythm||area)&&cfmt.infoline){set_font("info");down2+=cfmt.infospace+gene.curfont.size +put_inf2r(lwidth,-down2+gene.curfont.size*.22,rhythm,area,'r')}} +if(info.P&&cfmt.writefields.indexOf('P')>=0){set_font("parts");i=cfmt.partsspace+gene.curfont.size+gene.curfont.pad +if(down1+i>down2) +down2=down1+i +else +down2+=i +xy_str(0,-down2+gene.curfont.size*.22,info.P) +down2+=gene.curfont.pad}else if(down1>down2){down2=down1} +vskip(down2+cfmt.musicspace)} +var output="",style='\ +\n.stroke{stroke:currentColor;fill:none}\ +\n.bW{stroke:currentColor;fill:none;stroke-width:1}\ +\n.bthW{stroke:currentColor;fill:none;stroke-width:3}\ +\n.slW{stroke:currentColor;fill:none;stroke-width:.7}\ +\n.slthW{stroke:currentColor;fill:none;stroke-width:1.5}\ +\n.sW{stroke:currentColor;fill:none;stroke-width:.7}\ +\n.box{outline:1px solid black;outline-offset:1px}',font_style='',posx=cfmt.leftmargin/cfmt.scale,posy=0,img={width:cfmt.pagewidth,lm:cfmt.leftmargin,rm:cfmt.rightmargin,wx:0,chg:1},defined_glyph={},defs='',fulldefs='',stv_g={scale:1,dy:0,st:-1,v:-1,g:0},blkdiv=0 +var tgls={"mtr ":{x:0,y:0,c:"\u0020"},brace:{x:0,y:0,c:"\ue000"},lphr:{x:0,y:23,c:"\ue030"},mphr:{x:0,y:23,c:"\ue038"},sphr:{x:0,y:26,c:"\ue039"},short:{x:0,y:32,c:"\ue038"},tick:{x:0,y:29,c:"\ue039"},rdots:{x:-1,y:0,c:"\ue043"},dsgn:{x:-12,y:0,c:"\ue045"},dcap:{x:-12,y:0,c:"\ue046"},sgno:{x:-5,y:0,c:"\ue047"},coda:{x:-10,y:0,c:"\ue048"},tclef:{x:-8,y:0,c:"\ue050"},cclef:{x:-8,y:0,c:"\ue05c"},bclef:{x:-8,y:0,c:"\ue062"},pclef:{x:-6,y:0,c:"\ue069"},spclef:{x:-6,y:0,c:"\ue069"},stclef:{x:-8,y:0,c:"\ue07a"},scclef:{x:-8,y:0,c:"\ue07b"},sbclef:{x:-7,y:0,c:"\ue07c"},oct:{x:0,y:2,c:"\ue07d"},oct2:{x:0,y:2,c:"\ue07e"},mtr0:{x:0,y:0,c:"\ue080"},mtr1:{x:0,y:0,c:"\ue081"},mtr2:{x:0,y:0,c:"\ue082"},mtr3:{x:0,y:0,c:"\ue083"},mtr4:{x:0,y:0,c:"\ue084"},mtr5:{x:0,y:0,c:"\ue085"},mtr6:{x:0,y:0,c:"\ue086"},mtr7:{x:0,y:0,c:"\ue087"},mtr8:{x:0,y:0,c:"\ue088"},mtr9:{x:0,y:0,c:"\ue089"},mtrC:{x:0,y:0,c:"\ue08a"},"mtr+":{x:0,y:0,c:"\ue08c"},"mtr(":{x:0,y:0,c:"\ue094"},"mtr)":{x:0,y:0,c:"\ue095"},HDD:{x:-7,y:0,c:"\ue0a0"},breve:{x:-7,y:0,c:"\ue0a1"},HD:{x:-5.2,y:0,c:"\ue0a2"},Hd:{x:-3.8,y:0,c:"\ue0a3"},hd:{x:-3.7,y:0,c:"\ue0a4"},ghd:{x:2,y:0,c:"\ue0a4",sc:.66},pshhd:{x:-3.7,y:0,c:"\ue0a9"},pfthd:{x:-3.7,y:0,c:"\ue0b3"},x:{x:-3.7,y:0,c:"\ue0a9"},"circle-x":{x:-3.7,y:0,c:"\ue0b3"},srep:{x:-5,y:0,c:"\ue101"},"dot+":{x:-5,y:0,sc:.7,c:"\ue101"},diamond:{x:-4,y:0,c:"\ue1b9"},triangle:{x:-4,y:0,c:"\ue1bb"},dot:{x:-1,y:0,c:"\ue1e7"},flu1:{x:-.3,y:0,c:"\ue240"},fld1:{x:-.3,y:0,c:"\ue241"},flu2:{x:-.3,y:0,c:"\ue242"},fld2:{x:-.3,y:0,c:"\ue243"},flu3:{x:-.3,y:3.5,c:"\ue244"},fld3:{x:-.3,y:-4,c:"\ue245"},flu4:{x:-.3,y:8,c:"\ue246"},fld4:{x:-.3,y:-9,c:"\ue247"},flu5:{x:-.3,y:12.5,c:"\ue248"},fld5:{x:-.3,y:-14,c:"\ue249"},"acc-1":{x:-1,y:0,c:"\ue260"},"cacc-1":{x:-18,y:0,c:"\ue26a\ue260\ue26b"},"sacc-1":{x:-1,y:0,sc:.7,c:"\ue260"},acc3:{x:-1,y:0,c:"\ue261"},"cacc3":{x:-18,y:0,c:"\ue26a\ue261\ue26b"},sacc3:{x:-1,y:0,sc:.7,c:"\ue261"},acc1:{x:-2,y:0,c:"\ue262"},"cacc1":{x:-18,y:0,c:"\ue26a\ue262\ue26b"},sacc1:{x:-2,y:0,sc:.7,c:"\ue262"},acc2:{x:-3,y:0,c:"\ue263"},"acc-2":{x:-3,y:0,c:"\ue264"},"acc-1_2":{x:-2,y:0,c:"\ue280"},"acc-3_2":{x:-3,y:0,c:"\ue281"},acc1_2:{x:-1,y:0,c:"\ue282"},acc3_2:{x:-3,y:0,c:"\ue283"},accent:{x:-3,y:2,c:"\ue4a0"},stc:{x:0,y:-2,c:"\ue4a2"},emb:{x:0,y:-2,c:"\ue4a4"},wedge:{x:0,y:0,c:"\ue4a8"},marcato:{x:-3,y:-2,c:"\ue4ac"},hld:{x:-7,y:-2,c:"\ue4c0"},brth:{x:0,y:0,c:"\ue4ce"},caes:{x:0,y:8,c:"\ue4d1"},r00:{x:-1.5,y:0,c:"\ue4e1"},r0:{x:-1.5,y:0,c:"\ue4e2"},r1:{x:-3.5,y:-6,c:"\ue4e3"},r2:{x:-3.2,y:0,c:"\ue4e4"},r4:{x:-3,y:0,c:"\ue4e5"},r8:{x:-3,y:0,c:"\ue4e6"},r16:{x:-4,y:0,c:"\ue4e7"},r32:{x:-4,y:0,c:"\ue4e8"},r64:{x:-4,y:0,c:"\ue4e9"},r128:{x:-4,y:0,c:"\ue4ea"},mrep:{x:-6,y:0,c:"\ue500"},mrep2:{x:-9,y:0,c:"\ue501"},p:{x:-3,y:0,c:"\ue520"},f:{x:-3,y:0,c:"\ue522"},pppp:{x:-15,y:0,c:"\ue529"},ppp:{x:-14,y:0,c:"\ue52a"},pp:{x:-8,y:0,c:"\ue52b"},mp:{x:-8,y:0,c:"\ue52c"},mf:{x:-8,y:0,c:"\ue52d"},ff:{x:-7,y:0,c:"\ue52f"},fff:{x:-10,y:0,c:"\ue530"},ffff:{x:-14,y:0,c:"\ue531"},sfz:{x:-10,y:0,c:"\ue539"},trl:{x:-5,y:-2,c:"\ue566"},turn:{x:-5,y:0,c:"\ue567"},turnx:{x:-5,y:0,c:"\ue569"},umrd:{x:-6,y:2,c:"\ue56c"},lmrd:{x:-6,y:2,c:"\ue56d"},dplus:{x:-3,y:0,c:"\ue582"},sld:{x:-8,y:12,c:"\ue5d0"},grm:{x:-3,y:-2,c:"\ue5e2"},dnb:{x:-3,y:0,c:"\ue610"},upb:{x:-2,y:0,c:"\ue612"},opend:{x:-2,y:-2,c:"\ue614"},roll:{x:0,y:0,c:"\ue618"},thumb:{x:-2,y:-2,c:"\ue624"},snap:{x:-2,y:-2,c:"\ue630"},ped:{x:-10,y:0,c:"\ue650"},pedoff:{x:-5,y:0,c:"\ue655"},mtro:{x:0,y:0,c:"\ue911"},mtrc:{x:0,y:0,c:"\ue915"},"mtr.":{x:0,y:0,c:"\ue920"},"mtr|":{x:0,y:0,c:"\ue925"},longa:{x:-4.7,y:0,c:"\ue95d"},custos:{x:-4,y:3,c:"\uea02"},ltr:{x:2,y:6,c:"\ueaa4"}} +var glyphs={} +function m_gl(s){return s.replace(/[Cco]\||[co]\.|./g,function(e){var m=tgls["mtr"+e] +return m?m.c:0})} +function def_use(gl){var i,j,g +if(defined_glyph[gl]) +return +defined_glyph[gl]=true;g=glyphs[gl] +if(!g){error(1,null,"Unknown glyph: '$1'",gl) +return} +j=0 +while(1){i=g.indexOf('xlink:href="#',j) +if(i<0) +break +i+=13;j=g.indexOf('"',i);def_use(g.slice(i,j))} +defs+='\n'+g} +function defs_add(text){var i,j,gl,tag,is,ie=0 +text=text.replace(//g,'') +while(1){is=text.indexOf('<',ie);if(is<0) +break +i=text.indexOf('id="',is) +if(i<0) +break +i+=4;j=text.indexOf('"',i);if(j<0) +break +gl=text.slice(i,j);ie=text.indexOf('>',j);if(ie<0) +break +if(text[ie-1]=='/'){ie++}else{i=text.indexOf(' ',is);if(i<0) +break +tag=text.slice(is+1,i);ie=text.indexOf('',ie) +if(ie<0) +break +ie+=3+tag.length} +if(text.substr(is,7)=='=0?staff_tb[st].staffscale:1 +if(st>=0&&new_scale!=1) +dy=staff_tb[st].y +else +dy=posy +if(new_scale==stv_g.scale&&dy==stv_g.dy) +return +stv_g.scale=new_scale;stv_g.dy=dy;stv_g.st=st;stv_g.v=-1;set_g()} +function set_scale(s){var new_dy,new_scale=s.p_v.scale +if(new_scale==1){set_sscale(s.st) +return} +new_dy=posy +if(staff_tb[s.st].staffscale!=1){new_scale*=staff_tb[s.st].staffscale;new_dy=staff_tb[s.st].y} +if(new_scale==stv_g.scale&&stv_g.dy==posy) +return +stv_g.scale=new_scale;stv_g.dy=new_dy;stv_g.st=staff_tb[s.st].staffscale==1?-1:s.st;stv_g.v=s.v;set_g()} +function set_dscale(st,no_scale){if(output){if(stv_g.started){stv_g.started=false +glout() +output+="\n"} +if(stv_g.st<0){staff_tb[0].output+=output}else if(stv_g.scale==1){staff_tb[stv_g.st].output+=output}else{staff_tb[stv_g.st].sc_out+=output} +output=""} +if(st<0) +stv_g.scale=1 +else +stv_g.scale=no_scale?1:staff_tb[st].staffscale;stv_g.st=st;stv_g.dy=0} +function delayed_update(){var st,new_out,text +for(st=0;st<=nstaff;st++){if(staff_tb[st].sc_out){output+='\n'+ +staff_tb[st].sc_out+'\n';staff_tb[st].sc_out=""} +if(!staff_tb[st].output) +continue +output+='\n'+ +staff_tb[st].output+'\n';staff_tb[st].output=""}} +function anno_out(s,t,f){if(s.istart==undefined) +return +var type=s.type,h=s.ymx-s.ymn+4,wl=s.wl||2,wr=s.wr||2 +if(s.grace) +type=C.GRACE +f(t||abc2svg.sym_name[type],s.istart,s.iend,s.x-wl-2,staff_tb[s.st].y+s.ymn+h-2,wl+wr+4,h,s)} +function a_start(s,t){anno_out(s,t,user.anno_start)} +function a_stop(s,t){anno_out(s,t,user.anno_stop)} +function empty_function(){} +var anno_start=empty_function,anno_stop=empty_function +function anno_put(){var s +while(1){s=anno_a.shift() +if(!s) +break +switch(s.type){case C.CLEF:case C.METER:case C.KEY:case C.REST:if(s.type!=C.REST||s.rep_nb){set_sscale(s.st) +break} +case C.GRACE:case C.NOTE:case C.MREST:set_scale(s) +break} +anno_stop(s)}} +function out_XYAB(str,x,y,a,b){x=sx(x);y=sy(y);output+=str.replace(/X|Y|A|B|F|G/g,function(c){switch(c){case'X':return x.toFixed(1) +case'Y':return y.toFixed(1) +case'A':return a +case'B':return b +case'F':return a.toFixed(1) +default:return b.toFixed(1)}})} +function g_open(x,y,rot,sx,sy){glout() +out_XYAB('\n';stv_g.g++} +function g_close(){glout() +stv_g.g--;output+='\n'} +Abc.prototype.out_svg=function(str){output+=str} +function sx(x){if(stv_g.g) +return x +return(x+posx)/stv_g.scale} +Abc.prototype.sx=sx +function sy(y){if(stv_g.g) +return-y +if(stv_g.scale==1) +return posy-y +if(stv_g.v>=0) +return(stv_g.dy-y)/voice_tb[stv_g.v].scale +return stv_g.dy-y} +Abc.prototype.sy=sy;Abc.prototype.sh=function(h){if(stv_g.st<0) +return h/stv_g.scale +return h} +Abc.prototype.ax=function(x){return x+posx} +Abc.prototype.ay=function(y){if(stv_g.st<0) +return posy-y +return posy+(stv_g.dy-y)*stv_g.scale-stv_g.dy} +Abc.prototype.ah=function(h){if(stv_g.st<0) +return h +return h*stv_g.scale} +function out_sxsy(x,sep,y){x=sx(x);y=sy(y);output+=x.toFixed(1)+sep+y.toFixed(1)} +Abc.prototype.out_sxsy=out_sxsy +function xypath(x,y,fill){if(fill) +out_XYAB('\n'}} +for(st=0;st<=nstaff;st++){p_st=staff_tb[st] +if(!p_st.hlu) +continue +set_sscale(st) +hlud(p_st.hlu,6) +hlud(p_st.hld,-6)}} +var gla=[[],[],"",[],[],[]] +function glout(){var e,v=[] +if(gla[0].length){while(1){e=gla[0].shift() +if(e==undefined) +break +v.push(e.toFixed(1))} +output+=''+gla[2]+'\n' +gla[2]=""} +if(!gla[3].length) +return +output+='\n'} +function xygl(x,y,gl){if(glyphs[gl]){def_use(gl) +out_XYAB('\n',x,y,gl)}else{var tgl=tgls[gl] +if(tgl){x+=tgl.x*stv_g.scale;y-=tgl.y +if(tgl.sc){out_XYAB('B\n',x,y,tgl.sc,tgl.c)}else{gla[0].push(sx(x)) +gla[1].push(sy(y)) +gla[2]+=tgl.c}}else{error(1,null,'no definition of $1',gl)}}} +function out_acciac(x,y,dx,dy,up){if(up){x-=1;y+=4}else{x-=5;y-=4} +out_XYAB('\n',x,y,dx,-dy)} +function out_brace(x,y,h){x+=posx-6;y=posy-y;h/=24;output+=''+tgls.brace.c+'\n'} +function out_bracket(x,y,h){x+=posx-5;y=posy-y-3;h+=2;output+='\n'} +function out_hyph(x,y,w){var n,a_y,d=25+((w/20)|0)*3 +if(w>15.) +n=((w-15)/d)|0 +else +n=0;x+=(w-d*n-5)/2;out_XYAB('\n',x,y+4,Math.round((d-5)/stv_g.scale),d*n+5)} +function out_stem(x,y,h,grace,nflags,straight){var dx=grace?GSTEM_XOFF:3.5,slen=-h +if(h<0) +dx=-dx;x+=dx*stv_g.scale +if(stv_g.v>=0) +slen/=voice_tb[stv_g.v].scale;gla[3].push(sx(x)) +gla[3].push(sy(y)) +gla[3].push(slen) +if(!nflags) +return +y+=h +if(h>0){if(!straight){if(!grace){xygl(x,y,"flu"+nflags) +return}else{output+='=0){out_XYAB('MX Yl7 3.2 0 3.2 -7 -3.2z\n',x,y);y-=5.4}}else{while(--nflags>=0){out_XYAB('MX Yl3 1.5 0 2 -3 -1.5z\n',x,y);y-=3}}}}else{if(!straight){if(!grace){xygl(x,y,"fld"+nflags) +return}else{output+='=0){out_XYAB('MX Yl7 -3.2 0 -3.2 -7 3.2z\n',x,y);y+=5.4}}}} +output+='"/>\n'} +function out_trem(x,y,ntrem){out_XYAB('\n'} +function out_tubr(x,y,dx,dy,up){var h=up?-3:3;y+=h;dx/=stv_g.scale;output+='\n'} +function out_tubrn(x,y,dx,dy,up,str){var dxx,sw=str.length*10,h=up?-3:3;set_font("tuplet") +xy_str(x+dx/2,y+dy/2-gene.curfont.size*.1,str,'c') +dx/=stv_g.scale +if(!up) +y+=6;output+='\n'+'\n'} +function out_wln(x,y,w){out_XYAB('\n',x,y+1,w)} +var deco_str_style={crdc:{dx:0,dy:5,style:'font:italic 14px text,serif',anchor:' text-anchor="middle"'},dacs:{dx:0,dy:3,style:'font:bold 15px text,serif',anchor:' text-anchor="middle"'},pf:{dx:0,dy:5,style:'font:italic bold 16px text,serif',anchor:' text-anchor="middle"'}} +deco_str_style.at=deco_str_style.crdc +function out_deco_str(x,y,de){var name=de.dd.glyph +if(name=='fng'){out_XYAB('\ +A\n',x-2,y,m_gl(de.dd.str)) +return} +if(name=='@'){name='at'}else if(!/^[A-Za-z][A-Za-z\-_]*$/.test(name)){error(1,de.s,"No function for decoration '$1'",de.dd.name) +return} +var f,a_deco=deco_str_style[name] +if(!a_deco) +a_deco=deco_str_style.crdc +else if(a_deco.style) +style+="\n."+name+"{"+a_deco.style+"}",delete a_deco.style +x+=a_deco.dx;y+=a_deco.dy;out_XYAB('',x,y,name,a_deco.anchor||"");set_font("annotation");out_str(de.dd.str) +output+='\n'} +function out_arp(x,y,val){g_open(x,y,270);x=0;val=Math.ceil(val/6) +while(--val>=0){xygl(x,6,"ltr");x+=6} +g_close()} +function out_cresc(x,y,val,defl){x+=val*stv_g.scale +val=-val;out_XYAB('\n' +else +output+='-4l'+(-val).toFixed(1)+' -4"/>\n'} +function out_dim(x,y,val,defl){out_XYAB('\n' +else +output+='-4l'+(-val).toFixed(1)+' -4"/>\n'} +function out_ltr(x,y,val){y+=4;val=Math.ceil(val/6) +while(--val>=0){xygl(x,y,"ltr");x+=6}} +Abc.prototype.out_lped=function(x,y,val,defl){if(!defl.nost) +xygl(x,y,"ped");if(!defl.noen) +xygl(x+val+6,y,"pedoff")} +function out_8va(x,y,val,defl){if(val<18){val=18 +x-=4} +if(!defl.nost){out_XYAB('8\ +va\n',x-8,y);x+=12;val-=12} +y+=6;out_XYAB('\n',x,y,val) +if(!defl.noen) +out_XYAB('\n',x+val,y)} +function out_8vb(x,y,val,defl){if(val<18){val=18 +x-=4} +if(!defl.nost){out_XYAB('8\ +vb\n',x-8,y);x+=10 +val-=10} +out_XYAB('\n',x,y,val) +if(!defl.noen) +out_XYAB('\n',x+val,y)} +function out_15ma(x,y,val,defl){if(val<25){val=25 +x-=6} +if(!defl.nost){out_XYAB('15\ +ma\n',x-10,y);x+=20;val-=20} +y+=6;out_XYAB('\n',x,y,val) +if(!defl.noen) +out_XYAB('\n',x+val,y)} +function out_15mb(x,y,val,defl){if(val<24){val=24 +x-=5} +if(!defl.nost){out_XYAB('15\ +mb\n',x-10,y);x+=18 +val-=18} +out_XYAB('\n',x,y,val) +if(!defl.noen) +out_XYAB('\n',x+val,y)} +var deco_val_tb={arp:out_arp,cresc:out_cresc,dim:out_dim,ltr:out_ltr,lped:function(x,y,val,defl){self.out_lped(x,y,val,defl)},"8va":out_8va,"8vb":out_8vb,"15ma":out_15ma,"15mb":out_15mb} +function out_deco_val(x,y,name,val,defl){if(deco_val_tb[name]) +deco_val_tb[name](x,y,val,defl) +else +error(1,null,"No function for decoration '$1'",name)} +function out_glisq(x2,y2,de){var ar,a,len,de1=de.start,x1=de1.x,y1=de1.y+staff_tb[de1.st].y,dx=x2-x1,dy=self.sh(y1-y2) +if(!stv_g.g) +dx/=stv_g.scale +ar=Math.atan2(dy,dx) +a=ar/Math.PI*180 +len=(dx-(de1.s.dots?13+de1.s.xmx:8) +-8-(de.s.notes[0].shac||0)) +/ Math.cos(ar) +g_open(x1,y1,a);x1=de1.s.dots?13+de1.s.xmx:8;len=len/6|0 +if(len<1) +len=1 +while(--len>=0){xygl(x1,0,"ltr");x1+=6} +g_close()} +function out_gliss(x2,y2,de){var ar,a,len,de1=de.start,x1=de1.x,y1=de1.y+staff_tb[de1.st].y,dx=x2-x1,dy=self.sh(y1-y2) +if(!stv_g.g) +dx/=stv_g.scale +ar=Math.atan2(dy,dx) +a=ar/Math.PI*180 +len=(dx-(de1.s.dots?13+de1.s.xmx:8) +-8-(de.s.notes[0].shac||0)) +/ Math.cos(ar) +g_open(x1,y1,a);xypath(de1.s.dots?13+de1.s.xmx:8,0) +output+='h'+len.toFixed(1)+'" stroke-width="1"/>\n';g_close()} +var deco_l_tb={glisq:out_glisq,gliss:out_gliss} +function out_deco_long(x,y,de){var s,p_v,m,nt,i,name=de.dd.glyph,de1=de.start +if(!deco_l_tb[name]){error(1,null,"No function for decoration '$1'",name) +return} +p_v=de.s.p_v +if(de.defl.noen){s=p_v.s_next +while(s&&!s.dur) +s=s.next +if(s){for(m=0;m<=s.nhd;m++){nt=s.notes[m] +if(!nt.a_dd) +continue +for(i=0;i'+ +p+'') +j=p.length>1?2:1 +w+=j*gene.curfont.swfac +dy=''} +str.push('=') +w+=cwidf('=') +if(s.tempo_ca){str.push(s.tempo_ca) +w+=strwh(s.tempo_ca)[0] +j=s.tempo_ca.length+1} +if(s.tempo){str.push(s.tempo) +w+=strwh(s.tempo.toString())[0]}else{p=tempo_note(s,s.new_beat) +str.push(''+ +p+'') +j=p.length>1?2:1 +w+=j*gene.curfont.swfac +dy='y'}} +if(s.tempo_str2){if(dy) +str.push(''+ +s.tempo_str2+'') +else +str.push(s.tempo_str2) +w+=strwh(s.tempo_str2)[0]} +s.tempo_str=str.join(' ') +w+=cwidf(' ')*(str.length-1) +s.tempo_wh=[w,13.0]} +function writempo(s,x,y){var bh +set_font("tempo") +if(gene.curfont.box){gene.curfont.box=false +bh=gene.curfont.size+4} +output+=''+s.tempo_str+'\n' +if(bh){gene.curfont.box=true +output+='\n'} +s.invis=true} +function vskip(h){posy+=h} +function svg_flush(){if(multicol||!output||!user.img_out||posy==0) +return +var i,font,fmt=tsnext?tsnext.fmt:cfmt,w=Math.ceil((fmt.trimsvg||fmt.singleline==1)?(cfmt.leftmargin+img.wx*cfmt.scale+cfmt.rightmargin+2):img.width),head='\n' +if(defs) +head+=''+defs+'\n\n' +if(cfmt.scale!=1){head+='\n';g='\n'} +if(psvg) +psvg.ps_flush(true);if(blkdiv>0){user.img_out(blkdiv==1?'
':'
') +blkdiv=-1} +user.img_out(head+output+g+"");output="" +font_style='' +if(cfmt.fullsvg){defined_glyph={} +for(i=0;i') +blkdiv=0}} +Abc.prototype.blk_flush=blk_flush +var par_sy,cur_sy,voice_tb,curvoice,staves_found,vover,tsfirst +function voice_filter(){var opt +function vfilt(opts,opt){var i,sel=new RegExp(opt) +if(sel.test(curvoice.id)||sel.test(curvoice.nm)){for(i=0;itime) +continue +w=w_tb[s.type] +if(s.type==C.GRACE&&s.next&&s.next.type==C.GRACE) +w-- +if(s.time127) +break +if(wmin==6) +b_chk() +ir=0 +while(1){v=vn[ir++] +if(v==undefined) +break +s=vtb[v] +if(!s||s.time!=time) +continue +w=w_tb[s.type] +if(!w&&s.type==C.GRACE&&s.next&&s.next.type==C.GRACE) +w-- +if(w!=wmin) +continue +if(!w&&s.type==C.PART){if(s.prev) +s.prev.next=s.next +else +s.p_v.sym=s.next +vtb[v]=s.next +if(s.next){s.next.part=s +s.next.prev=s.prev +if(s.soln) +s.next.soln=1} +continue} +if(s.type==C.STAVES) +new_sy=s.sy +if(fl){fl=0;s.seqst=true} +s.ts_prev=prev +prev.ts_next=s +prev=s +vtb[v]=s.next} +if(wmin) +fl=1}} +function voice_adj(sys_chg){var p_voice,s,s2,v,sl +function set_feathered_beam(s1){var s,s2,t,d,b,i,a,d=s1.dur,n=1 +for(s=s1;s;s=s.next){if(s.beam_end||!s.next) +break +n++} +if(n<=1){delete s1.feathered_beam +return} +s2=s;b=d/2;a=d/(n-1);t=s1.time +if(s1.feathered_beam>0){for(s=s1,i=n-1;s!=s2;s=s.next,i--){d=((a*i)|0)+b;s.dur=d;s.time=t;t+=d}}else{for(s=s1,i=0;s!=s2;s=s.next,i++){d=((a*i)|0)+b;s.dur=d;s.time=t;t+=d}} +s.dur=s.time+s.dur-t;s.time=t} +if(curvoice&&curvoice.clone){parse.istart=parse.eol +do_cloning()} +if(par_sy.one_v) +fill_mr_ba(voice_tb[par_sy.top_voice]) +for(v=0;v=staves_found) +break} +for(;s;s=s.next){if(w_tb[s.type]<5&&s.type!=C.STAVES&&s.type!=C.CLEF&&s.time&&(!s.prev||s.time>s.prev.time+s.prev.dur)){s2={type:C.BAR,bar_type:"[]",v:s.v,p_v:s.p_v,st:s.st,time:s.time,dur:0,next:s,prev:s.prev,fmt:s.fmt,invis:1} +if(s.prev) +s.prev.next=s2 +else +voice_tb[s.v].sym=s2 +s.prev=s2} +switch(s.type){case C.GRACE:if(!cfmt.graceword) +continue +for(s2=s.next;s2;s2=s2.next){switch(s2.type){case C.SPACE:continue +case C.NOTE:if(!s2.a_ly) +break +s.a_ly=s2.a_ly;s2.a_ly=null +break} +break} +continue} +if(s.feathered_beam) +set_feathered_beam(s)}}} +function new_syst(init){var st,v,sy_staff,p_voice,sy_new={voices:[],staves:[],top_voice:0} +if(init){cur_sy=par_sy=sy_new +return} +for(v=0;vptim+wmeasure&&s.prev.type!=C.MREST) +return 1 +for(s3=s.next;s3&&s3.time==s.time;s3=s3.next);for(;s3&&!s3.bar_type;s3=s3.next);return s3&&(s3.time-bar_tim)%wmeasure} +for(s=tsfirst;;s=s.ts_next){if(!s) +return +switch(s.type){case C.METER:wmeasure=s.wmeasure +case C.CLEF:case C.KEY:case C.STBRK:continue +case C.BAR:if(s.bar_num) +bar_num=s.bar_num +break} +break} +for(s2=s.ts_next;s2;s2=s2.ts_next){if(s2.type==C.BAR&&s2.time){if(s2.timetim) +break +if(!s2.bar_type) +continue +if(s2.bar_type!='[') +nu=0 +if(s2.text) +txt=s2.text} +if(s.bar_num){bar_num=s.bar_num +ptim=bar_tim=tim +break} +if(wmeasure==1){if(s.bar_dotted) +break +if(txt){if(!cfmt.contbarnb){if(txt[0]=='1') +rep_tim=bar_num +else +bar_num=rep_tim}} +if(!nu) +s.bar_num=++bar_num +break} +n=bar_num+(tim-bar_tim)/wmeasure +k=n-(n|0) +if(cfmt.checkbars&&k&&check_meas()) +error(0,s,"Bad measure duration") +if(tim>ptim+wmeasure){n|=0 +k=0 +bar_tim=tim +bar_num=n} +if(txt){if(txt[0]=='1'){if(!cfmt.contbarnb) +rep_tim=tim-bar_tim +if(!nu) +s.bar_num=n}else{if(!cfmt.contbarnb) +bar_tim=tim-rep_tim +n=bar_num+(tim-bar_tim)/wmeasure +if(n==(n|0)) +s.bar_num=n}}else{n|=0 +s.bar_num=n} +if(!k) +ptim=tim +break}}} +function not2abc(pit,acc){var i,nn='' +if(acc&&acc!=3){if(typeof acc!="object"){nn=['__','_','','^','^^'][acc+2]}else{i=acc[0] +if(i>0){nn+='^'}else{nn+='_' +i=-i} +nn+=i+'/'+acc[1]}} +nn+=ntb[(pit+75)%7] +for(i=pit;i>=23;i-=7) +nn+="'" +for(i=pit;i<16;i+=7) +nn+="," +return nn} +function get_map(text){if(!text) +return +var i,note,notes,map,tmp,ns,ty='',a=text.split(/\s+/) +if(a.length<3){syntax(1,not_enough_p) +return} +ns=a[1] +if(ns[0]=='*'||ns.indexOf("all")==0){ns='all'}else{if(ns.indexOf("octave,")==0||ns.indexOf("key,")==0){ty=ns[0] +ns=ns.split(',')[1] +ns=ns.replace(/[,']+/,'').toUpperCase() +if(ns.indexOf("key,")==0) +ns=ns.replace(/[=^_]+/,'')} +tmp=new scanBuf +tmp.buffer=ns +note=parse_acc_pit(tmp) +if(!note){syntax(1,"Bad note in %%map") +return} +ns=ty+not2abc(note.pit,note.acc)} +notes=maps[a[0]] +if(!notes) +maps[a[0]]=notes={} +map=notes[ns] +if(!map) +notes[ns]=map=[] +a.shift() +a.shift() +if(!a.length) +return +a=info_split(a.join(' ')) +i=0 +if(a[0].indexOf('=')<0){if(a[0][0]!='*'){tmp=new scanBuf;tmp.buffer=a[0];map[1]=parse_acc_pit(tmp)} +if(!a[1]) +return +i++ +if(a[1].indexOf('=')<0){map[0]=a[1].split(',') +i++}} +for(;i=0){var val=parseInt(param) +if(isNaN(val)||val<-36||val>36){syntax(1,errs.bad_transp) +return} +val+=36 +val=((val/12|0)-3)*40+abc2svg.isb40[val%12] +if(param.slice(-1)=='b') +val+=4 +return val}} +Abc.prototype.do_pscom=function(text){var h1,val,s,cmd,param,n,k,b +cmd=text.match(/[^\s]+/) +if(!cmd) +return +cmd=cmd[0];if(curvoice&&curvoice.ignore){switch(cmd){case"staves":case"score":break +default:return}} +param=text.replace(cmd,'').trim() +if(param.slice(-5)==' lock'){fmt_lock[cmd]=true;param=param.slice(0,-5).trim()}else if(fmt_lock[cmd]){return} +switch(cmd){case"clef":if(parse.state>=2){s=new_clef(param) +if(s) +get_clef(s)} +return +case"deco":deco_add(param) +return +case"linebreak":set_linebreak(param) +return +case"map":get_map(param) +return +case"maxsysstaffsep":case"sysstaffsep":if(parse.state==3){val=get_unit(param) +if(isNaN(val)){syntax(1,errs.bad_val,"%%"+cmd) +return} +par_sy.voices[curvoice.v][cmd[0]=='m'?"maxsep":"sep"]=val +return} +break +case"multicol":switch(param){case"start":case"new":case"end":break +default:syntax(1,"Unknown keyword '$1' in %%multicol",param) +return} +s={type:C.BLOCK,subtype:"mc_"+param,dur:0} +if(parse.state>=2){curvoice=voice_tb[0] +curvoice.eoln=1 +sym_link(s) +return} +set_ref(s) +self.block_gen(s) +return +case"ottava":if(parse.state!=3) +return +n=parseInt(param) +if(isNaN(n)||n<-2||n>2||(!n&&!curvoice.ottava)){syntax(1,errs.bad_val,"%%ottava") +return} +k=n +if(n){curvoice.ottava=n}else{n=curvoice.ottava +curvoice.ottava=0} +a_dcn.push(["15mb","8vb","","8va","15ma"][n+2] ++(k?'(':')')) +return +case"repbra":if(curvoice) +curvoice.norepbra=!get_bool(param) +return +case"repeat":if(parse.state!=3) +return +if(!curvoice.last_sym){syntax(1,"%%repeat cannot start a tune") +return} +if(!param.length){n=1;k=1}else{b=param.split(/\s+/);n=parseInt(b[0]);k=parseInt(b[1]) +if(isNaN(n)||n<1||(curvoice.last_sym.type==C.BAR&&n>2)){syntax(1,"Incorrect 1st value in %%repeat") +return} +if(isNaN(k)){k=1}else{if(k<1){syntax(1,"Incorrect 2nd value in %%repeat") +return}}} +parse.repeat_n=curvoice.last_sym.type==C.BAR?n:-n;parse.repeat_k=k +return +case"sep":var h2,len,values,lwidth;set_page();lwidth=img.width-img.lm-img.rm;h1=h2=len=0 +if(param){values=param.split(/\s+/);h1=get_unit(values[0]) +if(values[1]){h2=get_unit(values[1]) +if(values[2]) +len=get_unit(values[2])} +if(isNaN(h1)||isNaN(h2)||isNaN(len)){syntax(1,errs.bad_val,"%%sep") +return}} +if(h1<1) +h1=14 +if(h2<1) +h2=h1 +if(len<1) +len=90 +if(parse.state>=2){s=new_block(cmd);s.x=(lwidth-len)/2/cfmt.scale;s.l=len/cfmt.scale;s.sk1=h1;s.sk2=h2 +return} +vskip(h1);output+='\n';vskip(h2);blk_flush() +return +case"setbarnb":val=parseInt(param) +if(isNaN(val)||val<1){syntax(1,"Bad %%setbarnb value") +break} +glovar.new_nbar=val +return +case"staff":if(parse.state!=3) +return +val=parseInt(param) +if(isNaN(val)){syntax(1,"Bad %%staff value '$1'",param) +return} +var st +if(param[0]=='+'||param[0]=='-') +st=curvoice.cst+val +else +st=val-1 +if(st<0||st>nstaff){syntax(1,"Bad %%staff number $1 (cur $2, max $3)",st,curvoice.cst,nstaff) +return} +delete curvoice.floating;curvoice.cst=st +return +case"staffbreak":if(parse.state!=3) +return +s={type:C.STBRK,dur:0} +if(param.slice(-1)=='f'){s.stbrk_forced=true +param=param.replace(/\sf$/,'')} +if(param){val=get_unit(param) +if(isNaN(val)){syntax(1,errs.bad_val,"%%staffbreak") +return} +s.xmx=val}else{s.xmx=14} +sym_link(s) +return +case"tacet":if(param[0]=='"') +param=param.slice(1,-1) +case"stafflines":case"staffscale":case"staffnonote":set_v_param(cmd,param) +return +case"staves":case"score":if(!parse.state) +return +if(parse.scores&&parse.scores.length>0){text=parse.scores.shift();cmd=text.match(/([^\s]+)\s*(.*)/);param=cmd[2] +cmd=cmd[1]} +get_staves(cmd,param) +return +case"center":case"text":k=cmd[0]=='c'?'c':cfmt.textoption +set_font("text") +if(parse.state>=2){s=new_block("text") +s.text=param +s.opt=k +s.font=cfmt.textfont +return} +write_text(param,k) +return +case"transpose":if(cfmt.sound) +return +val=get_transp(param) +if(val==undefined){val=get_interval(param) +if(val==undefined) +return} +switch(parse.state){case 0:cfmt.transp=0 +case 1:cfmt.transp=(cfmt.transp||0)+val +return} +curvoice.shift=val +key_trans() +return +case"tune":return +case"user":set_user(param) +return +case"voicecolor":if(curvoice) +curvoice.color=param +return +case"vskip":val=get_unit(param) +if(isNaN(val)){syntax(1,errs.bad_val,"%%vskip") +return} +if(val<0){syntax(1,"%%vskip cannot be negative") +return} +if(parse.state>=2){s=new_block(cmd);s.sk=val +return} +vskip(val);return +case"newpage":case"leftmargin":case"rightmargin":case"pagescale":case"pagewidth":case"printmargin":case"scale":case"staffwidth":if(parse.state>=2){s=new_block(cmd);s.param=param +return} +if(cmd=="newpage"){blk_flush() +if(user.page_format) +blkdiv=2 +return} +break} +self.set_format(cmd,param)} +Abc.prototype.do_begin_end=function(type,opt,text){var i,j,action,s +switch(type){case"js":js_inject(text) +break +case"ml":if(cfmt.pageheight){syntax(1,"Cannot have %%beginml with %%pageheight") +break} +if(parse.state>=2){s=new_block(type);s.text=text}else{blk_flush() +if(user.img_out) +user.img_out(text)} +break +case"svg":j=0 +while(1){i=text.indexOf('',i) +j=text.indexOf('',i) +if(j<0){syntax(1,"No in %%beginsvg sequence") +break} +style+=text.slice(i+1,j).replace(/\s+$/,'')} +j=0 +while(1){i=text.indexOf('\n',j) +if(i<0) +break +j=text.indexOf('',i) +if(j<0){syntax(1,"No in %%beginsvg sequence") +break} +defs_add(text.slice(i+6,j))} +break +case"text":action=get_textopt(opt);if(!action) +action=cfmt.textoption +set_font("text") +if(text.indexOf('\\')>=0) +text=cnv_escape(text) +if(parse.state>1){s=new_block(type);s.text=text +s.opt=action +s.font=cfmt.textfont +break} +write_text(text,action) +break}} +function generate(){var s,v,p_voice;if(a_dcn.length){syntax(1,"Decoration without symbol") +a_dcn=[]} +if(parse.tp){syntax(1,"No end of tuplet") +s=parse.tps +if(s) +delete s.tp +delete parse.tp} +if(vover){syntax(1,"No end of voice overlay");get_vover(vover.bar?'|':')')} +voice_adj();sort_all() +if(tsfirst){for(v=0;v7){n-=12 +curvoice.tr_sco+=4} +s.k_sf=n +for(b40=0;b40<40;b40++){if(abc2svg.b40l5[b40]==n) +break} +s.k_b40=b40 +if(!s.k_a_acc) +return +d=b40-s.orig.k_b40 +a_acc=[] +for(i=0;imxt){p_v2=voice_tb[v] +mxt=p_v2.time}} +if(p_v.time>=mxt) +return +var p_v_sav=curvoice,dur=mxt-p_v.time,s={type:C.MREST,stem:0,multi:0,nhd:0,xmx:0,frm:1,dur:dur,dur_orig:dur,nmes:dur/p_v.wmeasure,notes:[{pit:18,dur:dur}],tacet:p_v.tacet},s2={type:C.BAR,bar_type:'|',dur:0,multi:0} +if(p_v2.last_sym.bar_type) +s2.bar_type=p_v2.last_sym.bar_type +glovar.mrest_p=1 +curvoice=p_v +sym_link(s) +sym_link(s2) +curvoice=p_v_sav} +function get_staves(cmd,parm){var s,p_voice,p_voice2,i,flags,v,vid,a_vf,st,range,nv=voice_tb.length,maxtime=0 +if(curvoice&&curvoice.clone){i=parse.eol +parse.eol=parse.bol +do_cloning() +parse.eol=i} +if(parm){a_vf=parse_staves(parm) +if(!a_vf) +return}else if(staves_found<0){syntax(1,errs.bad_val,'%%'+cmd) +return} +for(v=0;vmaxtime) +maxtime=p_voice.time} +if(!maxtime){par_sy.staves=[] +par_sy.voices=[]}else{voice_adj(true) +for(v=0;v0&&p_voice.norepbra==undefined&&!(par_sy.staves[st-1].flags&STOP_BAR)) +p_voice.norepbra=true} +curvoice=parse.state>=2?voice_tb[par_sy.top_voice]:null} +function clone_voice(id){var v,p_voice +for(v=0;vvover.p_voice.time) +vover.p_voice.time=curvoice.time} +curvoice.acc=[] +p_voice2=vover.p_voice +s=curvoice.last_sym +if(s.type==C.SPACE&&p_voice2.last_sym.type!=C.SPACE){s.p_v=p_voice2 +s.v=s.p_v.v +while(s.prev.type==C.SPACE){s=s.prev +s.p_v=p_voice2 +s.v=s.p_v.v} +s2=s.prev +s2.next=null +s.prev=p_voice2.last_sym +s.prev.next=s +p_voice2.last_sym=curvoice.last_sym +curvoice.last_sym=s2} +curvoice=p_voice2 +vover=null +return} +if(type=='('){if(vover){syntax(1,"Voice overlay already started") +return} +vover={p_voice:curvoice,time:curvoice.time} +return} +if(!curvoice.last_note){syntax(1,errs.nonote_vo) +return} +curvoice.last_note.beam_end=true;p_voice2=curvoice.voice_down +if(!p_voice2){p_voice2=clone_voice(curvoice.id+'o');curvoice.voice_down=p_voice2;p_voice2.time=0;p_voice2.second=true;p_voice2.last_note=null +v2=p_voice2.v;if(par_sy.voices[curvoice.v]){par_sy.voices[v2]={st:curvoice.st,second:true} +range=par_sy.voices[curvoice.v].range +for(v=0;vrange) +par_sy.voices[v].range++} +par_sy.voices[v2].range=range+1}} +p_voice2.ulen=curvoice.ulen +p_voice2.dur_fact=curvoice.dur_fact +p_voice2.acc=[] +if(!vover){time=p_voice2.time +if(curvoice.ignore) +s=null +else +for(s=curvoice.last_sym;;s=s.prev){if(s.type==C.BAR||s.time<=time) +break} +vover={bar:(s&&s.bar_type)?s.bar_type:'|',p_voice:curvoice,time:s?s.time:curvoice.time}}else{if(curvoice!=vover.p_voice&&curvoice.time!=vover.p_voice.time){syntax(1,"Wrong duration in voice overlay") +if(curvoice.time>vover.p_voice.time) +vover.p_voice.time=curvoice.time}} +p_voice2.time=vover.time;curvoice=p_voice2} +function is_voice_sig(){var s +if(curvoice.time) +return false +if(!curvoice.last_sym) +return true +for(s=curvoice.last_sym;s;s=s.prev) +if(w_tb[s.type]) +return false +return true} +function get_clef(s){var s2,s3 +if(s.clef_type=='p'){s2=curvoice.ckey +s2.k_drum=1 +s2.k_sf=0 +s2.k_b40=2 +s2.k_map=abc2svg.keys[7] +if(!curvoice.key) +curvoice.key=s2} +if(!curvoice.time&&is_voice_sig()){curvoice.clef=s +s.fmt=cfmt +return} +if(s.clef_none) +s2=null +else +for(s2=curvoice.last_sym;s2&&s2.time==curvoice.time;s2=s2.prev){if(w_tb[s2.type]) +break} +if(s2&&s2.time==curvoice.time&&s2.k_sf!=undefined){s3=s2 +s2=s2.prev} +if(s2&&s2.time==curvoice.time&&s2.bar_type&&s2.bar_type[0]!=':') +s3=s2 +if(s3){s2=curvoice.last_sym +curvoice.last_sym=s3.prev +sym_link(s) +s.next=s3 +s3.prev=s +curvoice.last_sym=s2 +if(s.soln){delete s.soln +curvoice.eoln=true}}else{sym_link(s)} +if(s.prev) +s.clef_small=1} +function get_key(parm){var v,p_voice,a=new_key(parm),s_key=a[0],s=s_key,empty=s.k_sf==undefined&&!s.k_a_acc +a=a[1] +if(empty) +s.invis=1 +else +s.orig=s +if(parse.state==1){parse.ckey=s +if(empty){s_key.k_sf=0;s_key.k_none=true +s_key.k_map=abc2svg.keys[7]} +for(v=0;v0) +vs=vid.split(',') +else +vs=[vid] +if(parse.state<2){while(1){vid=vs.shift() +if(!vid) +break +if(a.length) +memo_kv_parm(vid,a) +if(vid!='*'&&parse.state==1) +curvoice=new_voice(vid)} +return} +if(vid=='*'){syntax(1,"Cannot have V:* in tune body") +return} +curvoice=new_voice(vs[0]) +if(vs.length>1){vs.shift() +curvoice.clone={vs:vs,a:a.slice(0),bol:parse.iend} +if(parse.file[curvoice.clone.bol-1]!=']') +curvoice.clone.bol++} +set_kv_parm(a) +key_trans() +v=curvoice.v +if(curvoice.new){delete curvoice.new +if(staves_found<0){curvoice.st=curvoice.cst=++nstaff;par_sy.nstaff=nstaff;par_sy.voices[v]={st:nstaff,range:v} +par_sy.staves[nstaff]={stafflines:curvoice.stafflines||"|||||",staffscale:1}}else if(!par_sy.voices[v]){curvoice.ignore=1 +return}} +if(!curvoice.filtered&&par_sy.voices[v]&&(parse.voice_opts||parse.tune_v_opts)){curvoice.filtered=true;voice_filter()}} +function goto_tune(){var v,p_voice +set_page();write_heading();if(glovar.new_nbar){gene.nbar=glovar.new_nbar +glovar.new_nbar=0}else{gene.nbar=1} +parse.state=3 +for(v=0;v=0){p_voice=voice_tb[v];delete p_voice.new;p_voice.st=p_voice.cst=v;par_sy.voices[v]={st:v,range:v} +par_sy.staves[v]={stafflines:p_voice.stafflines||"|||||",staffscale:1}}}} +function get_sym(p,cont){var s,c,i,j,d +if(curvoice.ignore) +return +if(cont){s=curvoice.sym_cont +if(!s){syntax(1,"+: symbol line without music") +return}}else{if(curvoice.sym_restart){curvoice.sym_start=curvoice.sym_restart;curvoice.sym_restart=null} +s=curvoice.sym_start +if(!s) +s=curvoice.sym +if(!s){syntax(1,"s: without music") +return}} +i=0 +while(1){while(p[i]==' '||p[i]=='\t') +i++;c=p[i] +if(!c) +break +switch(c){case'|':while(s&&s.type!=C.BAR) +s=s.next +if(!s){syntax(1,"Not enough measure bars for symbol line") +return} +s=s.next;i++ +continue +case'!':case'"':j=++i +i=p.indexOf(c,j) +if(i<0){syntax(1,c=='!'?"No end of decoration":"No end of chord symbol/annotation");i=p.length +continue} +d=p.slice(j-1,i+1) +break +case'*':break +default:d=c.charCodeAt(0) +if(d<128){d=char_tb[d] +if(d.length>1&&(d[0]=='!'||d[0]=='"')){c=d[0] +break}} +syntax(1,errs.bad_char,c) +break} +while(s&&s.type!=C.NOTE) +s=s.next +if(!s){syntax(1,"Too many elements in symbol line") +return} +switch(c){default:break +case'!':a_dcn.push(d.slice(1,-1)) +deco_cnv(s,s.prev) +break +case'"':parse_gchord(d) +if(a_gch) +csan_add(s) +break} +s=s.next;i++} +curvoice.sym_cont=s} +function get_lyrics(text,cont){var s,word,p,i,j,ly,dfnt,ln,c,cf +if(curvoice.ignore) +return +if((curvoice.pos.voc&0x07)!=C.SL_HIDDEN) +curvoice.have_ly=true +if(cont){s=curvoice.lyric_cont +if(!s){syntax(1,"+: lyric without music") +return} +dfnt=get_font("vocal") +if(gene.deffont!=dfnt){if(gene.curfont==gene.deffont) +gene.curfont=dfnt +gene.deffont=dfnt}}else{set_font("vocal") +if(curvoice.lyric_restart){curvoice.lyric_start=s=curvoice.lyric_restart;curvoice.lyric_restart=null;curvoice.lyric_line=0}else{curvoice.lyric_line++;s=curvoice.lyric_start} +if(!s) +s=curvoice.sym +if(!s){syntax(1,"w: without music") +return}} +p=text;i=0 +cf=gene.curfont +while(1){while(p[i]==' '||p[i]=='\t') +i++ +if(!p[i]) +break +ln=0 +j=parse.istart+i+2 +switch(p[i]){case'|':while(s&&s.type!=C.BAR) +s=s.next +if(!s){syntax(1,"Not enough measure bars for lyric line") +return} +s=s.next;i++ +continue +case'-':case'_':word=p[i] +ln=p[i]=='-'?2:3 +break +case'*':word="" +break +default:word="";while(1){if(!p[i]) +break +switch(p[i]){case'_':case'*':case'|':i-- +case' ':case'\t':break +case'~':word+=' ' +i++ +continue +case'-':ln=1 +break +case'\\':if(!p[++i]) +continue +word+=p[i++] +continue +case'$':word+=p[i++] +c=p[i] +if(c=='0') +gene.curfont=gene.deffont +else if(c>='1'&&c<='9') +gene.curfont=get_font("u"+c) +default:word+=p[i++] +continue} +break} +break} +while(s&&s.type!=C.NOTE) +s=s.next +if(!s){syntax(1,"Too many words in lyric line") +return} +if(word&&(s.pos.voc&0x07)!=C.SL_HIDDEN){ly={t:word,font:cf,istart:j,iend:j+word.length} +if(ln) +ly.ln=ln +if(!s.a_ly) +s.a_ly=[] +s.a_ly[curvoice.lyric_line]=ly +cf=gene.curfont} +s=s.next;i++} +curvoice.lyric_cont=s} +function ly_set(s){var i,j,ly,d,s1,s2,p,w,spw,xx,sz,shift,dw,s3=s,wx=0,wl=0,n=0,dx=0,a_ly=s.a_ly,align=0 +for(s2=s.ts_next;s2;s2=s2.ts_next){if(s2.shrink){dx+=s2.shrink +n++} +if(s2.bar_type){dx+=3 +break} +if(!s2.a_ly) +continue +i=s2.a_ly.length +while(--i>=0){ly=s2.a_ly[i] +if(!ly) +continue +if(!ly.ln||ly.ln<2) +break} +if(i>=0) +break} +for(i=0;i]*>/g,'') +if(ly.ln>=2){ly.shift=0 +continue} +spw=cwid(' ')*ly.font.swfac +w=ly.t.wh[0] +if(s.type==C.GRACE){shift=s.wl}else if((p[0]>='0'&&p[0]<='9'&&p.length>2)||p[1]==':'||p[0]=='('||p[0]==')'){if(p[0]=='('){sz=spw}else{j=p.indexOf(' ') +set_font(ly.font) +if(j>0) +sz=strwh(p.slice(0,j))[0] +else +sz=w*.2} +shift=(w-sz)*.4 +if(shift>14) +shift=14 +shift+=sz +if(p[0]>='0'&&p[0]<='9'){if(shift>align) +align=shift}}else{shift=w*.4 +if(shift>14) +shift=14} +ly.shift=shift +if(shift>wl) +wl=shift +w+=spw*1.5 +w-=shift +if(w>wx) +wx=w} +while(!s3.seqst) +s3=s3.ts_prev +if(s3.ts_prev&&s3.ts_prev.bar_type) +wl-=4 +if(s3.wl0){for(i=0;i='0'&&ly.t[0]<='9') +ly.shift=align}}} +function draw_lyric_line(p_voice,j,y){var p,lastx,w,s,s2,ly,lyl,ln,hyflag,lflag,x0,shift +if(p_voice.hy_st&(1<=2){if(x0==0&&lastx>s.x-18) +lastx=s.x-18 +if(ln==2) +hyflag=true +else +lflag=true;x0=s.x-shift +continue} +x0=s.x-shift;if(ln) +hyflag=true +if(user.anno_start||user.anno_stop){s2={p_v:s.p_v,st:s.st,istart:ly.istart,iend:ly.iend,ts_prev:s,ts_next:s.ts_next,x:x0,y:y,ymn:y,ymx:y+gene.curfont.size,wl:0,wr:w} +anno_start(s2,'lyrics')} +xy_str(x0,y,p) +anno_stop(s2,'lyrics') +lastx=x0+w} +if(hyflag){hyflag=false;x0=realwidth-10 +if(x00){if(y>-tsfirst.fmt.vocalspace) +y=-tsfirst.fmt.vocalspace;y*=sc +for(j=0;j=0;){draw_lyric_line(p_voice,j,y+a_h[j]*.22) +y+=a_h[j]*1.1} +return y/sc} +function draw_all_lyrics(){var p_voice,s,v,nly,i,x,y,w,a_ly,ly,lyst_tb=new Array(nstaff+1),nv=voice_tb.length,h_tb=new Array(nv),nly_tb=new Array(nv),above_tb=new Array(nv),rv_tb=new Array(nv),top=0,bot=0,st=-1 +for(v=0;vy) +bot=y +while(nlyh_tb[v][i]) +h_tb[v][i]=ly.t.wh[1]}}}else{y=y_get(p_voice.st,1,0,realwidth) +if(topy) +bot=y} +if(!lyst_tb[st]) +lyst_tb[st]={} +lyst_tb[st].top=top;lyst_tb[st].bot=bot;nly_tb[v]=nly +if(nly==0) +continue +if(p_voice.pos.voc) +above_tb[v]=(p_voice.pos.voc&0x07)==C.SL_ABOVE +else if(voice_tb[v+1]&&voice_tb[v+1].st==st&&voice_tb[v+1].have_ly) +above_tb[v]=true +else +above_tb[v]=false +if(above_tb[v]) +lyst_tb[st].a=true +else +lyst_tb[st].b=true} +i=0 +for(v=0;v0) +lyst_tb[st].bot=draw_lyrics(p_voice,nly_tb[v],h_tb[v],lyst_tb[st].bot,1)} +while(--i>=0){v=rv_tb[i];p_voice=voice_tb[v];st=p_voice.st;set_dscale(st,true);lyst_tb[st].top=draw_lyrics(p_voice,nly_tb[v],h_tb[v],lyst_tb[st].top,-1)} +for(v=0;v0){for(s=p_voice.sym;s;s=s.next){if(s.a_ly){y_set(st,0,s.x-2,10,bot)}}}else{y_set(st,0,0,realwidth,bot)}}}} +function parse_gchord(type){var c,text,gch,x_abs,y_abs,i,j,istart,iend,ann_font=get_font("annotation"),h_ann=ann_font.size,line=parse.line +function get_float(){var txt='' +while(1){c=text[i++] +if("1234567890.-".indexOf(c)<0) +return parseFloat(txt) +txt+=c}} +istart=parse.bol+line.index +if(type.length>1){text=type.slice(1,-1);iend=istart+1}else{i=++line.index +while(1){j=line.buffer.indexOf('"',i) +if(j<0){syntax(1,"No end of chord symbol/annotation") +return} +if(line.buffer[j-1]!='\\'||line.buffer[j-2]=='\\') +break +i=j+1} +text=cnv_escape(line.buffer.slice(line.index,j)) +line.index=j +iend=parse.bol+line.index+1} +if(ann_font.pad) +h_ann+=ann_font.pad +i=0;type='g' +while(1){c=text[i] +if(!c) +break +gch={text:"",istart:istart,iend:iend,font:ann_font} +switch(c){case'@':type=c;i++;x_abs=get_float() +if(c!=','){syntax(1,"',' lacking in annotation '@x,y'");y_abs=0}else{y_abs=get_float() +if(c!=' ') +i--} +gch.x=x_abs;gch.y=y_abs +break +case'^':gch.pos=C.SL_ABOVE +case'_':if(c=='_') +gch.pos=C.SL_BELOW +case'<':case'>':i++;type=c +break +default:switch(type){case'g':gch.font=get_font("gchord") +gch.pos=curvoice.pos.gch||C.SL_ABOVE +break +case'^':gch.pos=C.SL_ABOVE +break +case'_':gch.pos=C.SL_BELOW +break +case'@':gch.x=x_abs;y_abs-=h_ann;gch.y=y_abs +break} +break} +gch.type=type +while(1){c=text[i] +if(!c) +break +switch(c){case'\\':c=text[++i] +if(c=='n') +break +gch.text+='\\' +if(!c) +break +default:gch.text+=c;i++ +continue +case'&':while(1){gch.text+=c;c=text[++i] +switch(c){default:continue +case';':case undefined:case'\\':break} +break} +if(c==';'){i++;gch.text+=c +continue} +break +case';':break} +i++ +break} +gch.otext=gch.text +if(!a_gch) +a_gch=[] +a_gch.push(gch)}} +function gch_tr1(p,tr){var i,o,n,ip,csa=p.split('/') +tr=abc2svg.b40l5[(tr+202)%40] +for(i=0;i=13?'##':n>=6?'#':n<=-9?'bb':n<=-2?'b':'') ++p.slice(ip)} +return csa.join('/')} +function csan_add(s){var i,gch +if(s.type==C.BAR){for(i=0;i8) +xspc=8;gch.x=-xspc;break +case'<':gch.x=-(wh[0]+6);y_left-=wh[1];gch.y=y_left+wh[1]/2 +break +case'>':gch.x=6;y_right-=wh[1];gch.y=y_right+wh[1]/2 +break}} +y_left/=2;y_right/=2 +for(ix=0;ix':gch.y-=y_right +break}}} +Abc.prototype.draw_gchord=function(i,s,x,y){if(s.invis&&s.play) +return +var y2,an=s.a_gch[i],h=an.text.wh[1],pad=an.font.pad,w=an.text.wh[0]+pad*2,dy=h*.22 +if(an.font.figb){h*=2.4 +dy+=an.font.size*1.3} +switch(an.type){case'_':y-=h+pad +break +case'^':y+=pad +break +case'<':case'>':if(an.type=='<'){if(s.notes[0].acc) +x-=s.notes[0].shac +x-=pad}else{if(s.xmx) +x+=s.xmx +if(s.dots) +x+=1.5+3.5*s.dots +x+=pad} +y+=(s.type==C.NOTE?(((s.notes[s.nhd].pit+s.notes[0].pit)>>1)- +18)*3:12) +-h/2 +break +default:if(y>=0) +y+=pad +else +y-=h+pad +break +case'@':y+=(s.type==C.NOTE?(((s.notes[s.nhd].pit+s.notes[0].pit)>>1)- +18)*3:12) +-h/2 +if(y>0){y2=y+h+pad+2 +if(y2>staff_tb[s.st].ann_top) +staff_tb[s.st].ann_top=y2}else{y2=y-2 +if(y2=0) +y_set(s.st,1,x,w,y+h+pad+2) +else +y_set(s.st,0,x,w,y-pad)} +use_font(an.font) +set_font(an.font) +set_dscale(s.st) +if(user.anno_start) +user.anno_start("annot",an.istart,an.iend,x-2,y+h+2,w+4,h+4,s) +xy_str(x,y+dy,an.text) +if(user.anno_stop) +user.anno_stop("annot",an.istart,an.iend,x-2,y+h+2,w+4,h+4,s)} +function draw_all_chsy(){var s,san1,an,i,x,y,w,n_an=0,minmax=new Array(nstaff+1) +function set_an_yu(j){var an,i,s,x,y,w +for(s=san1;s;s=s.ts_next){an=s.a_gch +if(!an) +continue +i=an.length-j-1 +an=an[i] +if(!an) +continue +if(an.pos==C.SL_ABOVE){x=s.x+an.x +w=an.text.wh[0] +if(w&&x+w>realwidth) +x=realwidth-w +y=y_get(s.st,1,x,w) +if(an.type=='g'&&yrealwidth) +x=realwidth-w +y=y_get(s.st,0,x,w)-2 +if(an.type=='g'&&y>minmax[s.st].ydn) +y=minmax[s.st].ydn +self.draw_gchord(i,s,x,y)}} +for(i=0;i<=nstaff;i++) +minmax[i]={ydn:staff_tb[i].botbar-3,yup:staff_tb[i].topbar+4} +for(s=tsfirst;s;s=s.ts_next){an=s.a_gch +if(!an) +continue +if(!san1) +san1=s +i=an.length +if(i>n_an) +n_an=i +while(--i>=0){if(an[i].type=='g'){an=an[i] +x=s.x+an.x +w=an.text.wh[0] +if(w&&x+w>realwidth) +x=realwidth-w +if(an.pos==C.SL_ABOVE){y=y_get(s.st,true,x,w) +if(y>minmax[s.st].yup) +minmax[s.st].yup=y}else if(an.pos==C.SL_BELOW){y=y_get(s.st,false,x,w)-2 +if(y=0) +return s_a[k].src.match(/.*\//)||''} +return""})() +user={errmsg:function(msg,l,c){errtxt+=clean_txt(msg)+'\n'},img_out:function(str){new_page+=str}} +function clean_txt(txt){return txt.replace(/<|>|&.*?;|&/g,function(c){switch(c){case'<':return"<" +case'>':return">" +case'&':return"&"} +return c})} +abc2svg.loadjs=function(fn,relay,onerror){var s=document.createElement('script') +if(/:\/\//.test(fn)) +s.src=fn +else +s.src=jsdir+fn +s.onload=relay +s.onerror=function(){if(onerror) +onerror(fn) +else +alert('error loading '+fn)} +document.head.appendChild(s)} +function render(){var i=0,j,k,res,re,re_stop=/\n<|\n%.begin/g,abc=new abc2svg.Abc(user);if(page.indexOf('",j) +tune=page.slice(j,k) +k+=10}else{re_stop.lastIndex=++j +while(1){res=re_stop.exec(page) +if(!res||res[0]=="\n<") +break +k=page.indexOf(res[0].replace("begin","end"),re_stop.lastIndex) +if(k<0) +break +re_stop.lastIndex=k} +if(!res||k<0) +k=page.length +else +k=re_stop.lastIndex-2 +tune=page.slice(j,k)} +new_page+='
'+
+clean_txt(tune)+'
\n\ +
\n' +try{abc.tosvg('abcdoc',tune)}catch(e){alert("abc2svg javascript error: "+e.message+"\nStack:\n"+e.stack)} +if(errtxt){i=page.indexOf("\n",j);i=page.indexOf("\n",i+1);alert("Errors in\n"+ +page.slice(j,i)+"\n...\n\n"+errtxt);errtxt=""} +abc2svg.abc_end();new_page+='

\n';i=k +if(k>=page.length) +break +re.lastIndex=i} +try{document.body.innerHTML=new_page+page.slice(i)}catch(e){alert("abc2svg bad generated SVG: "+e.message+"\nStack:\n"+e.stack)}} +page=document.body.innerHTML;abc2svg.abc_end=function(){} +if(abc2svg.modules.load(page,render)) +render()} +function dom_loaded(){if(typeof abc2svg!="object"||!abc2svg.modules){setTimeout(dom_loaded,500) +return} +abcdoc()} +document.addEventListener("DOMContentLoaded",dom_loaded,false)})() diff --git a/lib/ChordPro/res/abc/abc2svg/abcemb-1.js b/lib/ChordPro/res/abc/abc2svg/abcemb-1.js new file mode 100644 index 00000000..80cffe11 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/abcemb-1.js @@ -0,0 +1,93 @@ +// abc2svg - ABC to SVG translator +// @source: https://chiselapp.com/user/moinejf/repository/abc2svg +// Copyright (C) 2014-2020 Jean-Francois Moine - LGPL3+ +//#javascript +window.onerror=function(msg,url,line){if(typeof msg=='string') +alert("window error: "+msg+"\nURL: "+url+"\nLine: "+line) +else if(typeof msg=='object') +alert("window error: "+msg.type+' '+msg.target.src) +else +alert("window error: "+msg) +return false} +var user +if(typeof abc2svg=="undefined") +var abc2svg={} +function dom_loaded(){var errtxt='',new_page='',playing,abcplay,page,a_src=[],a_pe=[],glop,old_gm,jsdir=document.currentScript?document.currentScript.src.match(/.*\//):(function(){var s_a=document.getElementsByTagName('script') +for(var k=0;k=0) +return s_a[k].src.match(/.*\//)||''} +return""})(),playconf={onend:function(){playing=false}} +user={errmsg:function(msg,l,c){errtxt+=clean_txt(msg)+'\n'},img_out:function(str){new_page+=str},page_format:true} +function clean_txt(txt){return txt.replace(/<|>|&.*?;|&/g,function(c){switch(c){case'<':return"<" +case'>':return">" +case'&':return"&"} +return c})} +function endplay(){playing=false} +abc2svg.playseq=function(seq){var outputs +if(!abcplay){if(typeof AbcPlay=="undefined"){abc2svg.playseq=function(){} +return} +abcplay=AbcPlay(playconf)} +if(playing){abcplay.stop();return} +playing=true +if(!a_pe[seq]){var abc=new abc2svg.Abc(user);abcplay.clear();abc.tosvg("play","%%play") +try{if(glop) +abc.tosvg("abcemb",page,glop[0],glop[1]);abc.tosvg("abcemb"+seq,page,a_src[seq][0],a_src[seq][1])}catch(e){alert(e.message+'\nabc2svg tosvg bug - stack:\n'+e.stack);playing=false;a_pe[seq]=null +return} +a_pe[seq]=abcplay.clear()} +abcplay.play(0,100000,a_pe[seq])} +abc2svg.loadjs=function(fn,relay,onerror){var s=document.createElement('script');if(/:\/\//.test(fn)) +s.src=fn +else +s.src=jsdir+fn;s.type='text/javascript' +if(relay) +s.onload=relay;s.onerror=onerror||function(){alert('error loading '+fn)} +document.head.appendChild(s)} +function render(){var i=0,j,k,res,abc,seq=0,re=/\n%abc|\nX:/g,re_stop=/\nX:|\n<|\n%.begin/g,select=window.location.hash.slice(1);if(typeof follow=="function") +user.anno_stop=function(){};abc=new abc2svg.Abc(user) +j=page.indexOf("=0){k=page.indexOf("")+6 +abc.mei2mus(page.slice(j,k)) +document.body.innerHTML=new_page +return} +if(typeof follow=="function") +follow(abc,user,playconf) +if(select){select=decodeURIComponent(select);select=page.search(select) +if(select<0) +select=0} +for(;;){res=re.exec(page) +if(!res) +break +j=re.lastIndex-res[0].length;new_page+=page.slice(i,j);re_stop.lastIndex=++j +while(1){res=re_stop.exec(page) +if(!res||res[0][1]!="%") +break +k=page.indexOf(res[0].replace("begin","end"),re_stop.lastIndex) +if(k<0) +break +re_stop.lastIndex=k} +if(!res||k<0) +k=page.length +else +k=re_stop.lastIndex-2;if(!select||page[j]!='X'||(select>=j&&select\n';a_src.push([j,k])}else if(!glop){glop=[j,k]} +try{abc.tosvg('abcemb',page,j,k)}catch(e){alert("abc2svg javascript error: "+e.message+"\nStack:\n"+e.stack)} +if(errtxt){new_page+='
'+
+errtxt+"
\n";errtxt=""} +abc2svg.abc_end() +if(page[j]=='X') +new_page+='
\n'} +i=k +if(i>=page.length) +break +if(page[i]=='X') +i-- +re.lastIndex=i} +try{document.body.innerHTML=new_page+page.slice(i)}catch(e){alert("abc2svg bad generated SVG: "+e.message+"\nStack:\n"+e.stack)} +delete user.img_out;old_gm=user.get_abcmodel;user.get_abcmodel=function(tsfirst,voice_tb,music_types,info){if(old_gm) +old_gm(tsfirst,voice_tb,music_types,info);abcplay.add(tsfirst,voice_tb)}} +page=document.body.innerHTML +if(!abc2svg.Abc){abc2svg.loadjs(page.indexOf("=0?"mei2svg-1.js":"abc2svg-1.js",dom_loaded) +return} +abc2svg.abc_end=function(){} +if(abc2svg.modules.load(page,render)) +render()} +window.addEventListener("load",function(){setTimeout(dom_loaded,500)}) diff --git a/lib/ChordPro/res/abc/abc2svg/abcemb1-1.js b/lib/ChordPro/res/abc/abc2svg/abcemb1-1.js new file mode 100644 index 00000000..1392e71e --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/abcemb1-1.js @@ -0,0 +1,148 @@ +// abc2svg - ABC to SVG translator +// @source: https://chiselapp.com/user/moinejf/repository/abc2svg +// Copyright (C) 2014-2019 Jean-Francois Moine - LGPL3+ +//#javascript +window.onerror=function(msg,url,line){if(typeof msg=='string') +alert("window error: "+msg+"\nURL: "+url+"\nLine: "+line) +else if(typeof msg=='object') +alert("window error: "+msg.type+' '+msg.target.src) +else +alert("window error: "+msg) +return false} +window.onbeforeprint=function(){var e=document.getElementById("dd") +if(e) +e.style.display="none"} +window.onafterprint=function(){var e=document.getElementById("dd") +if(e) +e.style.display="block"} +var user +if(typeof abc2svg=="undefined") +var abc2svg={} +function dom_loaded(){var errtxt='',app="abcemb",new_page,playing,abcplay,tune_dur,scroll_to,dt,sY,page,pe,jsdir=document.currentScript?document.currentScript.src.match(/.*\//):(function(){var s_a=document.getElementsByTagName('script') +for(var k=0;k=0) +return s_a[k].src.match(/.*\//)||''} +return""})(),playconf={onend:function(){playing=false}} +user={errmsg:function(msg,l,c){errtxt+=clean_txt(msg)+'\n'},get_abcmodel:function(tsfirst,voice_tb){var d,i,n,pf,s=tsfirst +while(1){if(s.tempo&&!pf){d=0;n=s.tempo_notes.length +for(i=0;i|&.*?;|&/g,function(c){switch(c){case'<':return"<" +case'>':return">" +case'&':return"&"} +return c})} +function do_scroll(old){if(!old){var d=document.documentElement;dt=tune_dur/d.scrollHeight +var ttop=dt*d.clientHeight/4 +document.getElementById("ss").style.display="block" +scroll_to=setTimeout(do_scroll,ttop*1000,1) +window.scrollTo(0,8);sY=0}else{if(sY==window.pageYOffset){document.getElementById("ss").style.display="none" +scroll_to=null +return} +sY=window.pageYOffset;window.scrollTo(0,sY+1);scroll_to=setTimeout(do_scroll,dt*1000,1)}} +window.onmouseup=function(event){var e=document.getElementById("dc") +if(e){if(event.target.className=="db") +e.classList.toggle("show") +else if(e.classList.contains("show")) +e.classList.remove("show")}} +abc2svg.src_upd=function(){page=document.getElementById('ta').value +abc2svg.get_sel()} +abc2svg.src_edit=function(){document.body.innerHTML='\ +\ +
\ + Apply - \ + Cancel '} +abc2svg.st_scroll=function(){if(scroll_to){clearTimeout(scroll_to);document.getElementById("ss").style.display="none" +scroll_to=null}else{scroll_to=setTimeout(do_scroll,500,0)}} +abc2svg.playseq=function(select){var outputs +if(!abcplay){delete user.img_out;user.get_abcmodel=function(tsfirst,voice_tb){abcplay.add(tsfirst,voice_tb)} +abcplay=AbcPlay(playconf)} +if(playing){if(scroll_to){clearTimeout(scroll_to);scroll_to=null} +abcplay.stop();return} +playing=true +if(!pe){var abc=new abc2svg.Abc(user);abcplay.clear();abc.tosvg("play","%%play") +if(select) +abc.tosvg(app,"%%select "+select) +try{abc.tosvg(app,page)}catch(e){alert(e.message+'\nabc2svg tosvg bug - stack:\n'+e.stack);playing=false;pe=null +return} +pe=abcplay.clear()} +if(document.documentElement.scrollHeight>window.innerHeight) +scroll_to=setTimeout(do_scroll,500,0);abcplay.play(0,100000,pe)} +abc2svg.loadjs=function(fn,relay,onerror){var s=document.createElement('script');if(/:\/\//.test(fn)) +s.src=fn +else +s.src=jsdir+fn;s.type='text/javascript' +if(relay) +s.onload=relay;s.onerror=onerror||function(){alert('error loading '+fn)} +document.head.appendChild(s)} +abc2svg.get_sel=function(){var j,k,n=0,i=0,t=(typeof list_head=="undefined"?"Tunes:":list_head)+'';document.body.innerHTML=t} +function render(){var select=window.location.hash.slice(1) +var sty=document.createElement('style') +sty.innerHTML='\ +.dd{position:fixed;top:0;bottom:0;right:0;height:40px;cursor:pointer;font-size:16px}\ +#ss{display:none;background-color:red}\ +.db{display:block;margin:5px; padding:5px;background-color:yellow}\ +.db:hover,.db:focus{background-color:lightgreen}\ +.dc{position:absolute;left:-70px;min-width:100px;display:none;background-color:yellow}\ +.dc label{display:block;padding:0 5px 0 5px;margin:2px}\ +.dc label:hover{outline:solid;outline-width:2px}\ +.show{display:block}' +document.head.appendChild(sty) +if(!select) +abc2svg.get_sel() +else +abc2svg.do_render(decodeURIComponent(select))} +abc2svg.do_render=function(select){if(typeof follow=="function") +user.anno_stop=function(){};abc=new abc2svg.Abc(user) +new_page="" +if(typeof follow=="function") +follow(abc,user,playconf) +if(select){if(typeof AbcPlay!="undefined") +new_page+='
' +abc.tosvg(app,"%%select "+select)}else if(typeof AbcPlay!="undefined"){new_page+='
'} +try{abc.tosvg(app,page)}catch(e){alert("abc2svg javascript error: "+e.message+"\nStack:\n"+e.stack)} +if(typeof AbcPlay!="undefined") +new_page+='
' +if(errtxt){new_page+='
'+
+errtxt+"
\n";errtxt=""} +new_page+='\ +
\ +\ +
\ +\ +\ +\ +
\ +
\ +' +try{document.body.innerHTML=new_page}catch(e){alert("abc2svg bad generated SVG: "+e.message+"\nStack:\n"+e.stack) +return} +setTimeout(function(){if(typeof AbcPlay!="undefined"||document.documentElement.scrollHeight<=window.innerHeight) +document.getElementById("play").style.display="none"},500)} +if(!abc2svg.Abc){abc2svg.loadjs("abc2svg-1.js",dom_loaded) +return} +page=document.body.innerHTML;abc2svg.abc_end=function(){} +if(abc2svg.modules.load(page,render)) +render()} +window.addEventListener("load",function(){setTimeout(dom_loaded,500)}) diff --git a/lib/ChordPro/res/abc/abc2svg/abcemb2-1.js b/lib/ChordPro/res/abc/abc2svg/abcemb2-1.js new file mode 100644 index 00000000..739961e6 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/abcemb2-1.js @@ -0,0 +1,73 @@ +// abc2svg - ABC to SVG translator +// @source: https://chiselapp.com/user/moinejf/repository/abc2svg +// Copyright (C) 2014-2019 Jean-Francois Moine - LGPL3+ +//#javascript +window.onerror=function(msg,url,line){if(typeof msg=='string') +alert("window error: "+msg+"\nURL: "+url+"\nLine: "+line) +else if(typeof msg=='object') +alert("window error: "+msg.type+' '+msg.target.src) +else +alert("window error: "+msg) +return false} +var errtxt='',elts,tunes='',indx=[],select,playing,abcplay,playconf={onend:endplay},a_pe=[],glop,old_gm,jsdir=document.currentScript?document.currentScript.src.match(/.*\//):(function(){var s_a=document.getElementsByTagName('script') +for(var k=0;k=0) +return s_a[k].src.match(/.*\//)||''} +return""})(),user={errmsg:function(msg,l,c){errtxt+=clean_txt(msg)+'\n'},img_out:function(str){new_page+=str},page_format:true} +function clean_txt(txt){return txt.replace(/<|>|&.*?;|&/g,function(c){switch(c){case'<':return"<" +case'>':return">" +case'&':return"&"} +return c})} +function endplay(){playing=false} +function playseq(i){var outputs +if(!abcplay){if(typeof AbcPlay=="undefined"){playseq=function(){} +return} +abcplay=AbcPlay(playconf)} +if(playing){abcplay.stop() +return} +playing=true +if(!a_pe[i]){abc=new abc2svg.Abc(user);abcplay.clear();abc.tosvg("play","%%play") +if(select) +abc.tosvg('abcemb2',select) +try{if(glop!=undefined) +abc.tosvg("abcemb2",tunes,indx[glop],indx[glop+1]);abc.tosvg("abcemb2-"+i,tunes,indx[i],indx[i+1])}catch(e){alert(e.message+'\nabc2svg tosvg bug - stack:\n'+e.stack);playing=false;a_pe[seq]=null +return} +a_pe[i]=abcplay.clear()} +abcplay.play(0,100000,a_pe[i])} +function dom_loaded(){function toabc(s){return s.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&').replace(/[ \t]+(%%)/g,'$1').replace(/[ \t]+(.:)/g,'$1')} +abc2svg.loadjs=function(fn,relay,onerror){var s=document.createElement('script');if(/:\/\//.test(fn)) +s.src=fn +else +s.src=jsdir+fn;s.type='text/javascript' +if(relay) +s.onload=relay;s.onerror=onerror||function(){alert('error loading '+fn)} +document.head.appendChild(s)} +elts=document.getElementsByClassName('abc') +for(var i=0;i=0&&j\n' +else if(glop==undefined) +glop=i +if(sel){abc.tosvg('abcemb2',select);sel=''} +try{abc.tosvg('abcemb2',tunes,indx[i],indx[i+1])}catch(e){alert("abc2svg javascript error: "+e.message+"\nStack:\n"+e.stack)} +if(errtxt){new_page+='
'+
+errtxt+"
\n";errtxt=""} +try{elts[i].innerHTML=new_page}catch(e){alert("abc2svg bad generated SVG: "+e.message+"\nStack:\n"+e.stack)} +if(j>=0&&j. + +// global variables + var abc_kb = false, // keyboard state + kbds // button/state + +// constants + var oct = 4, + key = { // US keyboard + "1": "C4", + "2": "D4", + "3": "E4", + "4": "F4", + "5": "G4", + "6": "A4", + "7": "B4", + "8": "z4", +// "-": "-", +// "=": "=", + q: "C2", + w: "D2", + e: "E2", + r: "F2", + t: "G2", + y: "A2", + u: "B2", + i: "z2", +// "[": "[", +// "]": "]", + a: "C", + s: "D", + d: "E", + f: "F", + g: "G", + h: "A", + j: "B", + k: "z", + "'": "_", + '\\': "#", + z: "C/", + x: "D/", + c: "E/", + v: "F/", + b: "G/", + n: "A/", + m: "B/", + ",": "z/" +// ".": "." +// "/": "/" + } + +function key_press(e) { + var st, en + + if (e.ctrlKey) + return + + var kc = e.which + if (kc == 96) { // '`' + abc_kb = !abc_kb; + e.stopImmediatePropagation(); + e.preventDefault(); + kbds.style.backgroundColor = abc_kb ? "#80ff80" : "#ffd0d0" + return + } + if (!abc_kb) + return + + var c = String.fromCharCode(kc), + s = document.getElementById("source") + + function get_note() { + if (!s.value) + return + st = s.selectionEnd + while (1) { + if (st == 0) + break + if (!s.value[--st].match(/[1-9,'/]/)) // ' + break + } + if (!s.value[st].match(/[A-Ga-gz]/)) + return // null + en = st + while (1) { + if (++en >= s.value.length) + break + if (!s.value[en].match(/[1-9,'/]/)) // ' + break + } + return s.value.slice(st, en) + } // get_note() + + // --- key_press --- + + if (key[c]) { + c = key[c] + if (c[0] >= "A" && c[0] <= "G") { + switch (oct) { + case 2: + c = c[0] + ",," + (c[1] ? c[1] : '') + break + case 3: + c = c[0] + "," + (c[1] ? c[1] : '') + break +// case 4: +// break + case 5: + c = c.toLowerCase() + break + case 6: + c = c[0].toLowerCase() + "'" + (c[1] ? c[1] : '') + break + } + } + } else { + switch (c) { +// case '\n': +// c = '\n' +// break + case 'l': + oct = 2; + c = null + break + case ';': + oct = 3; + c = null + break + case 'o': + oct = 4; + c = null + break + case "p": + oct = 5; + c = null + break + case '9': + oct = 6; + c = null + break + case '+': // music dot + c = get_note() + if (!c) + return + if (c.length == 1) + c += "3/" + else + c = c.replace(/2|3|4|6|\/\/|\//, function(a) { + switch (a) { + case '2': return "3" + case '3': return "7/" + case '4': return "6" + case '6': return "7" + case '//': return "3///" + case '/': return "3//" + default: return "3/" + } + }) + s.value = s.value.slice(0, st) + c + + s.value.slice(en); + st += c.length; + s.setSelectionRange(st, st) + c = null + break + case ' ': + c = e.shiftKey ? ' ' : '|' + break + default: + if (kc == 0x08) { // del (KO vimb) + c = get_note() + if (c) { + s.value = s.value.slice(0, st) + + s.value.slice(en); + s.setSelectionRange(st, st); + c = null; + src_change() + break + } + } + return + } + } + if (c) { + st = s.selectionStart; + s.value = s.value.slice(0, st) + c + s.value.slice(st); + st += c.length; + s.setSelectionRange(st, st) + } + e.stopImmediatePropagation(); + e.preventDefault(); + src_change() +} + +function kbd_init() { + +// set the keyboard event in the ABC source +document.getElementById("source").addEventListener("keypress", key_press); + +// add a keyboard help window + var tmp = document.createElement("div"); + + tmp.id = "abckbd"; + tmp.className = "popup"; + tmp.style.width = "600px"; + tmp.innerHTML = +'
x
\ +\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
kbd-
-chg
C4D4E4F4G4A4B4z4O6-+
=
C2D2E2F2G2A2B2z2O4O5[]
C D E F G A BzO2O3b#
C/D/E/F/G/A/B/z/./
space (beam break)
| (measure bar)
'; + document.body.appendChild(tmp); + +// add a button to the right of 'help' that gives the keyboard state +// and raises the keyboard layout + kbds = document.createElement("li"); + kbds.addEventListener('click', function() {popshow('abckbd', true)}); + kbds.className = "dropbutton"; + kbds.style.backgroundColor ="#ffd0d0"; + kbds.innerHTML = 'ABC kbd'; + document.getElementById("nav").appendChild(kbds); + +// add a help about the ABC keyboard + tmp = document.createElement("div"); + tmp.id = "kbdhelp"; + tmp.className = "popup"; + tmp.style.width = "600px"; + tmp.innerHTML = +'
x
\ +
    \ +
  • The ABC keyboard is enabled/disabled by pressing the key \'`\'.
    \ +The layout of the keyboard is displayed by clicking on the keyboard state
    \ +(to the right of help).
  • \n\ +
  • When the keyboard is enabled:\ +
      \ +
    • non-shift/non-control characters are replaced by a ABC sequence,
    • \ +
    • \'(shift-)+\' adds a music dot,
    • \ +
    • \'space\' adds a measure bar,
    • \ +
    • \'shift-space\' adds a beam break.
    • \ +
  • \ +
'; + document.body.appendChild(tmp); + +// add a button to display the help about the keyboard + tmp = document.createElement("li"); + tmp.addEventListener('click', function() { popshow('kbdhelp', true)}); + tmp.innerHTML = 'Keyboard help'; + var help = document.getElementById("ha").parentNode; + help.insertBefore(tmp, help.childNodes[2]) +} + +kbd_init() diff --git a/lib/ChordPro/res/abc/abc2svg/abckbd2-1.js b/lib/ChordPro/res/abc/abc2svg/abckbd2-1.js new file mode 100644 index 00000000..5eeb5a10 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/abckbd2-1.js @@ -0,0 +1,302 @@ +// abckbd2.js - other ABC keyboard +// +// Copyright (C) 2014-2017 Jean-Francois Moine +// +// This file is part of abc2svg. +// +// abc2svg is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// abc2svg is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with abc2svg. If not, see . + +// global variables + var abc_kb = false, // keyboard state + kbds, // button/state + oct = 4, + dur = 4, // crotchet (quarter/black note) + chord + +// constants + var + dur_tb = ["////", "///", "//", "/", "", "2", "4", "8", "16" ], + key = { // US keyboard + // keyboard switch + "`": "`", + + // left: bar, accidentals, duration, octave + a: "|", s: "=", d: "d-", f: "d+", + z: "_", x: "^", c: "o-", v: "o+", + + // right: notes/rest + j: "C", k : "D", l : "E", ";": "F", + m: "G", ",": "A", ".": "B", "/": "z", + + // other keys + g: "[]", // chord start/stop + h: ".", // music dot + "\n": "\n" + } + +// stop the keypress event +function stop_key(e) { + e.stopImmediatePropagation(); + e.preventDefault(); + src_change() // rendering update +} + +// update the octave and duration flags +function kbd_upd() { + var c = chord ? '[' : "" + + switch (oct) { + case 2: c += "C,,"; break + case 3: c += "C,"; break +// case 4: + default: c += "C"; break + case 5: c += "c"; break + case 6: c += "c'"; break + case 7: c += "c''"; break + } + kbds.innerHTML = 'kbd ' + c + dur_tb[dur] +} + +// hadle a keypress +function key_press(e) { + if (e.ctrlKey) + return + + var st, en, + kc = e.which, + c = String.fromCharCode(kc), + s = document.getElementById("source") + + // check if there is a note/chord/rest before the text cursor + // if yes, return the start and stop indexes in 'st' & 'en' + function get_note() { + var ch + if (!s.value) + return // no symbol + en = st = s.selectionEnd + while (1) { + if (st == 0) + break + if (!s.value[--st].match(/[1-9,'/\]]/)) // ' + break + if (s.value[st] == ']') + ch = true + } + if (!s.value[st].match(/[A-Ga-gz]/)) + return // null + if (ch) { + while (1) { + if (st == 0) + return // null + if (s.value[--st] == '[') + break + } + } + return s.value.slice(st, en) + } // get_note() + + // --- key_press --- + + c = key[c] + switch (c) { + case undefined: + if (kc == 0x08) // 'delete' key + break + return + case '`': // keyboard switch + abc_kb = !abc_kb; + kbds.style.backgroundColor = abc_kb ? "#80ff80" : "#ffd0d0"; + stop_key(e) + return + } + if (!abc_kb) + return + + if (kc == 0x08) { // 'delete' key + c = get_note() + if (!c) + return + s.value = s.value.slice(0, st) + s.value.slice(en); + s.setSelectionRange(st, st); + stop_key(e) + return + } + + switch (c[0]) { + case "A": // notes + case "B": + case "C": + case "D": + case "E": + case "F": + case "G": + switch (oct) { + case 2: c = c + ",,"; break + case 3: c = c + ","; break +// case 4: break + case 5: c = c.toLowerCase(); break + case 6: c = c.toLowerCase() + "'"; break + case 7: c = c.toLowerCase() + "''"; break + } + if (!chord) + c += dur_tb[dur] + break + case "z": // rest + c += dur_tb[dur] + break + case "d": // duration + if (c[1] == '+') { + if (dur < dur_tb.length - 1) + dur++ + } else { + if (dur > 0) + dur-- + } + kbd_upd(); + c = null + break + case "o": // octave + if (c[1] == '+') { + if (oct < 7) + oct++ + } else { + if (oct > 2) + oct-- + } + kbd_upd(); + c = null + break + case '[': // chord start/stop + chord = !chord; + kbd_upd(); + c = chord ? '[' : (']' + dur_tb[dur]) + break + case '.': // music dot + c = get_note() + if (!c) + break + if (!c.match(/2|3|4|6|\/\/|\//)) + c += "3/" + else + c = c.replace(/2|3|4|6|\/\/|\//, function(a) { + switch (a) { + case '2': return "3" + case '3': return "7/" + case '4': return "6" + case '6': return "7" + case '//': return "3///" + case '/': return "3//" + default: return "3/" + } + }) + s.value = s.value.slice(0, st) + c + s.value.slice(en); + st += c.length; + s.setSelectionRange(st, st); + c = null + break + } + if (c) { + st = s.selectionStart; + s.value = s.value.slice(0, st) + c + s.value.slice(st); + st += c.length; + s.setSelectionRange(st, st) + } + stop_key(e) +} + +function kbd_init() { + +// set the keyboard event in the ABC source +document.getElementById("source").addEventListener("keypress", key_press); + +// add a keyboard help window + var tmp = document.createElement("div"); + + tmp.id = "abckbd"; + tmp.className = "popup"; + tmp.style.width = "600px"; + tmp.innerHTML = +'
x
\ +\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
kbd-
-chg
       
| = d- d+ [] dotC D E F
_ ^ o- o+ G A B z
space
'; + document.body.appendChild(tmp); + +// add a button under 'File' that gives the keyboard state +// and raises the keyboard layout + kbds = document.createElement("li"); + kbds.addEventListener('click', function() {popshow('abckbd', true)}); + kbds.className = "dropbutton"; + kbds.style.backgroundColor = "#ffd0d0"; + kbds.innerHTML = 'kbd c2'; + document.getElementById("er").parentNode.appendChild(kbds); + +// add a help about the ABC keyboard + tmp = document.createElement("div"); + tmp.id = "kbdhelp"; + tmp.className = "popup"; + tmp.style.width = "600px"; + tmp.innerHTML = +'
x
\ +
    \ +
  • The ABC keyboard is enabled/disabled by pressing the key \'`\'.
    \ +The layout of the keyboard is displayed by clicking on the keyboard state
    \ +(below File).
  • \ +
  • When the keyboard is enabled:\ +
      \ +
    • the right hand enters the notes and rests,
    • \ +
    • the left hand enters the measure bars and the accidentals,
      \ + and also controls the duration \'d-/d+\'\ + and the octave \'o-/o+\',
    • \ +
    • \'dot\' adds a music dot,
    • \ +
    • \'[]\' starts/stops chords,
    • \ +
    • \'space\' inserts a beam break.
    • \ +
  • \ +
'; + document.body.appendChild(tmp); + +// add a button to display the help about the keyboard + tmp = document.createElement("li"); + tmp.addEventListener('click', function() { popshow('kbdhelp', true)}); + tmp.innerHTML = 'Keyboard help'; + var help = document.getElementById("ha").parentNode; + help.insertBefore(tmp, help.childNodes[2]); + + kbd_upd() +} + +kbd_init() diff --git a/lib/ChordPro/res/abc/abc2svg/abcweb-1.js b/lib/ChordPro/res/abc/abc2svg/abcweb-1.js new file mode 100644 index 00000000..75c2a812 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/abcweb-1.js @@ -0,0 +1,243 @@ +//abcweb-1.js file to include in html pages +window.onerror=function(msg,url,line){if(typeof msg=='string') +alert("window error: "+msg+"\nURL: "+url+"\nLine: "+line) +else if(typeof msg=='object') +alert("window error: "+msg.type+' '+msg.target.src) +else +alert("window error: "+msg) +return false} +var user,abcplay +if(typeof abc2svg=="undefined") +var abc2svg={} +abc2svg.mu="" +abc2svg.abc_end=function(){} +abc2svg.jsdir=document.currentScript?document.currentScript.src.match(/.*\//):(function(){var s_a=document.getElementsByTagName('script') +for(var k=0;k=0) +return s_a[k].src.match(/.*\//)||''} +return""})() +abc2svg.loadjs=function(fn,relay,onerror){var s=document.createElement('script') +if(/:\/\//.test(fn)) +s.src=fn +else +s.src=abc2svg.jsdir+fn +s.onload=relay +s.onerror=function(){if(onerror) +onerror(fn) +else +alert('error loading '+fn)} +document.head.appendChild(s)} +function dom_loaded(){var abc,src,outb,err,a_inc={},tune_lst=[],html,busy,playing,playconf={onend:function(){playing=0}} +function visible(){var mu,r,wh=window.innerHeight||document.documentElement.clientHeight +while(1){mu=abc2svg.alldiv[0] +if(!mu) +break +r=mu.d.getBoundingClientRect() +if(r.top>wh) +break +musgen(mu) +abc2svg.alldiv.shift()} +if(abc2svg.alldiv.length){if(!abc2svg.onscroll){abc2svg.onscroll=visible +window.addEventListener("scroll",visible)}}else{window.removeEventListener("scroll",visible)}} +function get_p(e){var i,j,k,r,o='',sh=document.styleSheets,s=e.style +c=e.getAttribute("class") +if(c){c='.'+c +for(i=0;i]* class="[^"]*abc[^"]*/.test(src)) +re='<[^>]* class="[^"]*abc[^"]*' +else +re='%abc-\\d|X:\\s*\\d' +re=new RegExp('(^|\n)('+re+')','g') +while(1){res=re.exec(src) +if(!res) +break +i=re.lastIndex-res[0].length +if(i>ss){out+=src.slice(ss,i) +html=1} +t=res[2] +if(t[0]=='<'){i=src.indexOf('>',i)+1 +j=res[2].indexOf(' ') +t=res[2].slice(1,j) +j=src.indexOf('',i) +ss=j+t.length+4}else{re_stop.lastIndex=i +while(1){res=re_stop.exec(src) +if(!res||res[0]=="\n<") +break +k=src.indexOf(res[0].replace("begin","end"),re_stop.lastIndex) +if(k<0) +break +re_stop.lastIndex=k} +if(!res||k<0) +j=src.length +else +j=re_stop.lastIndex-1 +ss=j} +out+='\n' +re.lastIndex=ss} +out+=src.slice(ss) +if(abc2svg.page&&html) +out+='\ +
\
+Printing may be bad because the file contains pure HTML and %%pageheight\
+
\n' +document.body.innerHTML=out} +function save_music(){var i,k,div,c,s,sa +abc2svg.music=[{t:"",n:"mus0"}] +k=location.search +if(k){k=k.substr(1).split("&") +for(i=0;i=sa.length) +break +c=get_p(s) +div=document.createElement('div') +if(s.text.indexOf('\nX:')<0){abc2svg.music[0].t+=c+s.innerHTML +if(!abc2svg.music[0].d) +abc2svg.music[0].d=div}else{abc2svg.music.push({n:"mus"+abc2svg.music.length,t:c+s.innerHTML,d:div})} +s.parentNode.replaceChild(div,s)}} +function musgen(mu){var t=mu.t +if(busy){mu.w=1 +return} +busy=1 +function render(){var i,j,e +outb=err="" +abc.tosvg(mu.n,t) +abc2svg.abc_end() +if(mu.d){if(err) +outb+='
'
++err+"
\n" +if(abc.cfmt().with_source&&outb) +outb='
'
++clean_txt(t)
++'
\n\ +
\n' ++outb ++'
\n' +mu.d.innerHTML=outb +mu.d.addEventListener('click',abc2svg.playseq) +e=mu.d.getElementsByTagName('svg') +for(i=0;i|&.*?;|&/g,function(c){switch(c){case'<':return"<" +case'>':return">" +case'&':return"&"} +return c})} +abc2svg.playseq=function(evt){if(playing){abcplay.stop() +return} +var i,j,svg=evt.target,e=svg +while(svg.tagName!='svg'){svg=svg.parentNode +if(!svg) +return} +i=svg.getAttribute('class') +if(!i) +return +i=i.match(/tune(\d+)/) +if(!i) +return +i=i[1] +if(!abcplay){if(typeof AbcPlay=="undefined"){abc2svg.playseq=function(){} +return} +if(abc.cfmt().soundfont) +playconf.sfu=abc.cfmt().soundfont +abcplay=AbcPlay(playconf)} +if(!tune_lst[i]){tune_lst[i]=abc.tunes[i] +abcplay.add(tune_lst[i][0],tune_lst[i][1],tune_lst[i][3])} +s=tune_lst[i][0] +i=e.getAttribute('class') +if(i) +i=i.match(/abcr _(\d+)_/) +if(i){i=i[1] +while(s&&s.istart!=i) +s=s.ts_next +if(!s){alert("play bug: no such symbol in the tune") +return}} +while(s&&!s.fname) +s=s.ts_next +for(i=1;i=0) +return s_a[k].src.match(/.*\//)||''} +return""})() +user={read_file:function(fn){return a_inc[fn]},errmsg:function(msg,l,c){errtxt+=clean_txt(msg)+'\n'},get_abcmodel:function(tsfirst,voice_tb){var d,i,n,pf,s=tsfirst +while(1){if(s.tempo&&!pf){d=0 +n=s.tempo_notes.length +for(i=0;i=0){i=s.indexOf('type="text/vnd.abc"',i) +if(i>0){i=s.indexOf('\n',i)+1 +j=s.indexOf('|&.*?;|&/g,function(c){switch(c){case'<':return"<" +case'>':return">" +case'&':return"&"} +return c})} +function do_scroll(old){var d,ttop +if(!old){d=document.documentElement +dt=tune_dur/d.scrollHeight +ttop=dt*d.clientHeight/4 +document.getElementById("ss").style.display="block" +scroll_to=setTimeout(do_scroll,ttop*1000,1) +window.scrollTo(0,8) +sY=0}else{if(sY==window.pageYOffset){document.getElementById("ss").style.display="none" +scroll_to=null +return} +sY=window.pageYOffset +window.scrollTo(0,sY+1) +scroll_to=setTimeout(do_scroll,dt*1000,1)}} +abc2svg.src_upd=function(){page=document.getElementById('ta').value +abc2svg.get_sel()} +abc2svg.src_edit=function(){document.body.innerHTML='\ +\ +
\ + Apply - \ + Cancel '} +abc2svg.st_scroll=function(){if(scroll_to){clearTimeout(scroll_to) +document.getElementById("ss").style.display="none" +scroll_to=null}else{scroll_to=setTimeout(do_scroll,500,0)}} +abc2svg.loadjs=function(fn,relay,onerror){var s=document.createElement('script') +if(/:\/\//.test(fn)) +s.src=fn +else +s.src=jsdir+fn +s.onload=relay +s.onerror=function(){if(onerror) +onerror(fn) +else +alert('error loading '+fn)} +document.head.appendChild(s)} +abc2svg.get_sel=function(){var j,k,n=0,i=0,t=(typeof list_head=="undefined"?"Tunes:":list_head)+'
    \n' +tt=typeof list_tail=="undefined"?"(all tunes)":list_tail +for(;;){i=page.indexOf("\nX:",i) +if(i<0) +break +k=page.indexOf("\n",++i) +j=page.indexOf("\nT:",i) +n++ +t+='
  • '+ +page.slice(i+2,k).replace(/%.*/,'') +if(j>0&&j'+tt+'
  • \n\ +
' +document.body.innerHTML=t +if(window.location.hash) +window.location.hash=''} +function render(){var select=window.location.hash.slice(1) +var sty=document.createElement('style') +sty.innerHTML='\ +.dd{position:fixed;top:0;bottom:0;right:0;height:40px;cursor:pointer;font-size:16px}\ +#ss{display:none;background-color:red}\ +.db{margin:5px;background-color:yellow}\ +.db:hover,.db:focus{background-color:lightgreen}\ +.dc{position:absolute;left:-70px;min-width:100px;display:none;background-color:yellow}\ +.dc label{display:block;padding:0 5px 0 5px;margin:2px}\ +.dc label:hover{outline:solid;outline-width:2px}\ +.show{display:block}' +document.head.appendChild(sty) +if(!select) +abc2svg.get_sel() +else +abc2svg.do_render(decodeURIComponent(select))} +abc2svg.do_render=function(select){if(typeof follow=="function") +user.anno_stop=function(){} +tune_lst=[] +abc=new abc2svg.Abc(user) +new_page="" +if(typeof follow=="function") +follow(abc,user,playconf) +if(select){abc.tosvg(app,"%%select "+select) +window.location.hash=encodeURIComponent(select)} +try{abc.tosvg(app,page)}catch(e){alert("abc2svg javascript error: "+e.message+"\nStack:\n"+e.stack)} +abc2svg.abc_end() +if(errtxt){new_page+='
'+
+errtxt+"
\n" +errtxt=""} +new_page+='\ +
\ +\ +\ +\ +
\ +\ +\ +\ +
\ +
\ +' +try{document.body.innerHTML=new_page}catch(e){alert("abc2svg bad generated SVG: "+e.message+"\nStack:\n"+e.stack) +return} +var elts=document.getElementsByTagName('svg') +for(var i=0;i=0) +return s_a[k].src.match(/.*\//)||''} +return""})() +abc2svg.loadjs=function(fn,relay,onerror){var s=document.createElement('script') +if(/:\/\//.test(fn)) +s.src=fn +else +s.src=abc2svg.jsdir+fn +s.onload=relay +s.onerror=function(){if(onerror) +onerror(fn) +else +alert('error loading '+fn)} +document.head.appendChild(s)} +function dom_loaded(){var abc,src,outb,err,a_inc={},tune_lst=[],html,busy,playing,playconf={onend:function(){playing=0}} +function visible(){var mu,r,wh=window.innerHeight||document.documentElement.clientHeight +while(1){mu=abc2svg.alldiv[0] +if(!mu) +break +r=mu.d.getBoundingClientRect() +if(r.top>wh) +break +musgen(mu) +abc2svg.alldiv.shift()} +if(abc2svg.alldiv.length){if(!abc2svg.onscroll){abc2svg.onscroll=visible +window.addEventListener("scroll",visible)}}else{window.removeEventListener("scroll",visible)}} +function get_p(e){var i,j,k,r,o='',sh=document.styleSheets,s=e.style +c=e.getAttribute("class") +if(c){c='.'+c +for(i=0;i]* class="[^"]*abc[^"]*/.test(src)) +re='<[^>]* class="[^"]*abc[^"]*' +else +re='%abc-\\d|X:\\s*\\d' +re=new RegExp('(^|\n)('+re+')','g') +while(1){res=re.exec(src) +if(!res) +break +i=re.lastIndex-res[0].length +if(i>ss){out+=src.slice(ss,i) +html=1} +t=res[2] +if(t[0]=='<'){i=src.indexOf('>',i)+1 +j=res[2].indexOf(' ') +t=res[2].slice(1,j) +j=src.indexOf('',i) +ss=j+t.length+4}else{re_stop.lastIndex=i +while(1){res=re_stop.exec(src) +if(!res||res[0]=="\n<") +break +k=src.indexOf(res[0].replace("begin","end"),re_stop.lastIndex) +if(k<0) +break +re_stop.lastIndex=k} +if(!res||k<0) +j=src.length +else +j=re_stop.lastIndex-1 +ss=j} +out+='\n' +re.lastIndex=ss} +if(abc2svg.page&&html) +out+='\ +
\
+Printing may be bad because the file contains pure HTML and %%pageheight\
+
\n' +document.body.innerHTML=out} +function save_music(){var i,k,div,c,s,sa +function save_spe(ty){var s,div +while(1){s=document.getElementsByTagName(ty)[0] +if(!s) +break +div=document.createElement('div') +abc2svg.music.push({n:'mus'+abc2svg.music.length,t:s.outerHTML,d:div}) +s.parentNode.replaceChild(div,s)}} +abc2svg.music=[{t:"",n:"mus0"}] +k=location.search +if(k){k=k.substr(1).split("&") +for(i=0;i=sa.length) +break +c=get_p(s) +div=document.createElement('div') +if(s.text.indexOf('\nX:')<0){abc2svg.music[0].t+=c+s.innerHTML +if(!abc2svg.music[0].d) +abc2svg.music[0].d=div}else{abc2svg.music.push({n:"mus"+abc2svg.music.length,t:c+s.innerHTML,d:div})} +s.parentNode.replaceChild(div,s)} +save_spe('mei')} +function musgen(mu){var t=mu.t +if(busy){mu.w=1 +return} +busy=1 +function render(){var i,j,e +outb=err="" +abc.tosvg(mu.n,t) +abc2svg.abc_end() +if(err) +outb+='
'
++err+"
\n" +if(abc.cfmt().with_source&&outb) +outb='
'
++clean_txt(t)
++'
\n\ +
\n' ++outb ++'
\n' +mu.d.innerHTML=outb +mu.d.addEventListener('click',abc2svg.playseq) +e=mu.d.getElementsByTagName('svg') +for(i=0;i|&.*?;|&/g,function(c){switch(c){case'<':return"<" +case'>':return">" +case'&':return"&"} +return c})} +abc2svg.playseq=function(evt){if(playing){abcplay.stop() +return} +var i,j,svg=evt.target,e=svg +while(svg.tagName!='svg'){svg=svg.parentNode +if(!svg) +return} +i=svg.getAttribute('class') +if(!i) +return +i=i.match(/tune(\d+)/) +if(!i) +return +i=i[1] +if(!abcplay){if(typeof AbcPlay=="undefined"){abc2svg.playseq=function(){} +return} +if(abc.cfmt().soundfont) +playconf.sfu=abc.cfmt().soundfont +abcplay=AbcPlay(playconf)} +if(!tune_lst[i]){tune_lst[i]=abc.tunes[i] +abcplay.add(tune_lst[i][0],tune_lst[i][1],tune_lst[i][3])} +s=tune_lst[i][0] +i=e.getAttribute('class') +if(i) +i=i.match(/abcr _(\d+)_/) +if(i){i=i[1] +while(s&&s.istart!=i) +s=s.ts_next +if(!s){alert("play bug: no such symbol in the tune") +return}} +while(s&&!s.fname) +s=s.ts_next +for(i=1;i=0?"mei2svg-1.js":"abc2svg-1.js",dom_loaded) +return} +if(src.indexOf('type="text/vnd.abc"')<0) +move_music(src) +save_music() +abc=new abc2svg.Abc(user) +if(typeof follow=="function") +follow(abc,user,playconf) +if(abc2svg.music[0].t){abc.tosvg(abc2svg.music[0].n,abc2svg.music[0].t) +if(abc.cfmt().with_source&&abc.cfmt().with_source.indexOf('nohead')<0){abc2svg.music[0].d.innerHTML='
'
++clean_txt(abc2svg.music[0].t)
++'
\n'}} +abc2svg.alldiv=[] +for(var i=1;imax) +max=s.notes[s.nhd].pit +if(s.notes[0].pit0){s.x-=26;this.set_scale(s);this.draw_note(s) +if(s.notes[1].pit-s.notes[0].pit>4){this.xypath(s.x,3*(s.notes[1].pit-18)+staff_tb[s.st].y);this.out_svg('v'+ +((s.notes[1].pit-s.notes[0].pit)*3).toFixed(1)+'" stroke-width=".6"/>\n')} +s.x+=26;s.nhd=0 +p_voice.clef.nhd=0} +of(p_voice)},set_pitch:function(of,last_s){of(last_s) +if(!last_s&&this.cfmt().ambitus) +abc2svg.ambitus.do_ambitus.call(this)},set_fmt:function(of,cmd,param){if(cmd=="ambitus"){this.cfmt().ambitus=param +return} +of(cmd,param)},set_glue:function(of,w){var v,s,voice_tb=this.get_voice_tb() +of(w) +if(this.cfmt().ambitus){for(v=0;v0){s.wl=40;s.wr=12}else{of(s)}},set_hooks:function(abc){abc.draw_symbols=abc2svg.ambitus.draw_symbols.bind(abc,abc.draw_symbols);abc.set_pitch=abc2svg.ambitus.set_pitch.bind(abc,abc.set_pitch);abc.set_format=abc2svg.ambitus.set_fmt.bind(abc,abc.set_format);abc.set_sym_glue=abc2svg.ambitus.set_glue.bind(abc,abc.set_sym_glue) +abc.set_width=abc2svg.ambitus.set_width.bind(abc,abc.set_width)}} +if(!abc2svg.mhooks) +abc2svg.mhooks={} +abc2svg.mhooks.ambitus=abc2svg.ambitus.set_hooks diff --git a/lib/ChordPro/res/abc/abc2svg/break-1.js b/lib/ChordPro/res/abc/abc2svg/break-1.js new file mode 100644 index 00000000..c6425063 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/break-1.js @@ -0,0 +1,42 @@ +//break.js-module to handle the%%break command +if(typeof abc2svg=="undefined") +var abc2svg={} +abc2svg.break={get_break:function(parm){var C=abc2svg.C,b,c,d,sq,a=parm.split(/[ ,]/),cfmt=this.cfmt() +if(!cfmt.break) +cfmt.break=[] +for(n=1;n=t) +break}}else{s=s.next} +if(s) +s.soln=true}},do_pscom:function(of,text){if(text.slice(0,6)=="break ") +abc2svg.break.get_break.call(this,text) +else +of(text)},set_bar_num:function(of){of() +if(this.cfmt().break) +abc2svg.break.do_break.call(this)},set_hooks:function(abc){abc.do_pscom=abc2svg.break.do_pscom.bind(abc,abc.do_pscom);abc.set_bar_num=abc2svg.break.set_bar_num.bind(abc,abc.set_bar_num)}} +if(!abc2svg.mhooks) +abc2svg.mhooks={} +abc2svg.mhooks.break=abc2svg.break.set_hooks diff --git a/lib/ChordPro/res/abc/abc2svg/capo-1.js b/lib/ChordPro/res/abc/abc2svg/capo-1.js new file mode 100644 index 00000000..c5518f22 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/capo-1.js @@ -0,0 +1,33 @@ +//capo.js-module to add a capo chord line +if(typeof abc2svg=="undefined") +var abc2svg={} +abc2svg.capo={icb40:[0,5,6,11,16,17,22,23,28,33,34,39],gch_build:function(of,s){var t,i,gch,gch2,i2,abc=this,p_v=abc.get_curvoice(),a_gch=s.a_gch +if(p_v.capo&&a_gch){t=p_v.capo +i=0 +while(1){gch=a_gch[i++] +if(!gch){of(s) +return} +if(gch.type=='g') +break} +gch2=Object.create(gch) +gch2.capo=false +gch2.text=abc.gch_tr1(gch2.text,-abc2svg.capo.icb40[t%12]) +if(!p_v.capo_first){p_v.capo_first=true +gch2.text+=" (capo: "+t.toString()+")"} +gch2.font=abc.get_font(abc.cfmt().capofont?"capo":"annotation") +a_gch.splice(i,0,gch2) +gch.capo=true} +of(s)},set_fmt:function(of,cmd,param){if(cmd=="capo"){this.set_v_param("capo_",param) +return} +of(cmd,param)},set_vp:function(of,a){var i,v,p_v=this.get_curvoice() +for(i=0;i0){if(ch[i][0]==b) +break} +ch=ch[i] +for(i=0;i=12)b=0;break +case"b":case"♭":b--;if(b<0)b=11;break}}}} +if(b==undefined) +b=0 +ch=chcr(b,ch) +n=ch.length +r+=trans +for(m=0;m=12?"+":" ")} +chnm[k]=[vch]}}else{chnm=abc2svg.ch_names} +vch={v:voice_tb.length,id:"_chord",time:0,sym:{type:C.REST,time:0},instr:cfmt.chord.prog||0,vol:cfmt.chord.vol||.6} +vch.last_sym=vch.sym +voice_tb.push(vch) +k=0 +for(i=0;i0){for(i=0;i0){for(i=1;i<=s.k_sf;i++){a_tb[bn_tb[i]+3]=1;a_tb[bn_tb[i]+12+3]=1} +if(bn+sc[s.k_mode]>8) +bn-=12 +for(i=7;--i>=0;){y=bn+sc[s.k_mode+i] +this.xygl(x,staffb+y*3,(y&1)?"wk":"bk");if(a_tb[y+3]) +this.xygl(x,staffb+y*3,"sht");switch(s.k_mode+i){case 3:case 7:case 10:x+=4.5 +break}}}else{for(i=s.k_sf;i<0;i++){a_tb[bn_tb[i+6]+3]=1;a_tb[bn_tb[i+6]+12+3]=1} +if(bn+sc[s.k_mode]>8) +bn-=12 +for(i=0;i<7;i++){y=bn+sc[s.k_mode+i] +this.xygl(x,staffb+y*3,(y&1)?"wk":"bk");if(a_tb[y+3]) +this.xygl(x,staffb+y*3,"flt");switch(s.k_mode+i){case 2:case 6:case 9:x+=4.5 +break}}}}else if(s.k_a_acc.length){for(i=0;i\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +') +for(v=0;v=sel.m) +break} +if(!s) +return +if(sel.sq){seq=sel.sq +for(s=s.ts_next;s;s=s.ts_next){if(s.type==C.BAR&&s.bar_num==sel.m){if(--seq==0) +break}} +if(!s) +return}} +if(sel.t==0) +return s;bar_time=s.time+sel.t +while(s.time0||cfmt.clip[0].t>0){s=go_global_time(s,cfmt.clip[0]) +if(!s){this.set_tsfirst(null) +return} +sy=this.get_cur_sy() +for(s2=this.get_tsfirst();s2!=s;s2=s2.ts_next){switch(s2.type){case C.CLEF:s2.p_v.clef=s2 +break +case C.KEY:s2.p_v.key=this.clone(s2.as.u.key) +break +case C.METER:s2.p_v.meter=this.clone(s2.as.u.meter) +break +case C.STAVES:sy=s2.sy;this.set_cur_sy(sy) +break}} +for(v=0;v. + +// user definitions +var user = { + read_file: function(fn) { // read a file (main or included) + var i, + p = fn, + file = abc2svg.readFile(p) + + if (!file && fn[0] != '/') { + for (i = 0; i < abc2svg.path.length; i++) { + p = abc2svg.path[i] + '/' + fn + file = abc2svg.readFile(p) + if (file) + break + } + } + + if (!file) + return file + + // memorize the file path + i = p.lastIndexOf('/') + if (i > 0) { + p = p.slice(0, i) + if (abc2svg.path.indexOf(p) < 0) + abc2svg.path.unshift(p) + } + + // convert the file content into a Unix string + i = file.indexOf('\r') + if (i >= 0) { + if (file[i + 1] == '\n') + file = file.replace(/\r\n/g, '\n') // M$ + else + fike = file.replace(/\r/g, '\n') // Mac + } + + // load the required modules (synchronous) + abc2svg.modules.load(file) + + return file + }, + errtxt: '', + errmsg: // print or store the error messages + typeof abc2svg.printErr == 'function' + ? function(msg, l, c) { abc2svg.printErr(msg) } + : function(msg, l, c) { user.errtxt += msg + '\n' } +} // user + +var abc // (global for 'toxxx.js') + +if (!abc2svg.path) + abc2svg.path = [] // path to ABC files - from env ABCPATH + +// treat a file +function do_file(fn) { + var file = user.read_file(fn) + + if (!file) { + if (fn != "default.abc") + user.errmsg("Cannot read file '" + fn + "'") + return + } +// if (typeof(utf_convert) == "function") +// file = utf_convert(file) + + if (fn.slice(-4) == ".mei") { + if (!abc.mei2mus) + abc2svg.abort(new Error("No MEI support")); + abc.mei2mus(file) + return + } + + // generate + try { + abc.tosvg(fn, file) + } catch (e) { + abc2svg.abort(e) + } +} // do_file() + +function abc_cmd(cmd, args, interp_name) { + var arg, parm, fn; + + abc2svg.abort = function(e) { + abc2svg.printErr('javascript error: ' + e.message + + '\nStack:\n' + e.stack) + abc2svg.quit() + } // abort() + + // put the last options before the last file + function arg_reorder(a) { + var f, + i = a.length - 2 + + while (i > 2 && a[i].slice(0, 2) == '--') + i -= 2 + f = a[--i] + a.splice(i, 1) + a.push(f) + } // arg_reorder() + + // if the first argument is a javascript file, load it + if (args[0] && args[0].slice(-3) == '.js') { + abc2svg.loadjs(args[0]) + args.shift() + } + + if (!args[0]) { + abc2svg.printErr('ABC translator with ' + interp_name + + ' and abc2svg-' + abc2svg.version + ' ' + + abc2svg.vdate + + '\nUsage:\n ' + cmd + + ' [script.js] [options] ABC_file [[options] ABC_file]* [options]\n\ +Arguments:\n\ + script.js generation script to load - default: tohtml.js (HTML+SVG)\n\ + options ABC options (the last options are moved before the last file)\n\ + ABC_file ABC file') + abc2svg.quit() + } + + // the default output is HTML+SVG + if (typeof abc2svg.abc_init != 'function') + abc2svg.loadjs("tohtml.js") + + // initialize the backend + abc = new abc2svg.Abc(user) + if (typeof global == "object" && !global.abc) + global.abc = abc + abc2svg.abc_init(args) + + // load 'default.abc' + try { + do_file("default.abc") + } catch (e) { + } + + // put the last options before the last ABC file + if (args.length > 2 && args[args.length - 2].slice(0, 2) == '--') + arg_reorder(args) + + while (1) { + arg = args.shift() + if (!arg) + break + if (arg[0] == "-" && arg[1] == "-") { + parm = arg.replace('--', 'I:') + " " + + args.shift() + "\n" + abc2svg.modules.load(parm) + abc.tosvg(cmd, parm) + } else { + do_file(arg) + abc.tosvg('cmd', '%%select\n') + } + } + abc2svg.abc_end() +} + +// nodejs +if (typeof module == 'object' && typeof exports == 'object') { + exports.user = user; + exports.abc_cmd = abc_cmd +} diff --git a/lib/ChordPro/res/abc/abc2svg/combine-1.js b/lib/ChordPro/res/abc/abc2svg/combine-1.js new file mode 100644 index 00000000..77ce0302 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/combine-1.js @@ -0,0 +1,110 @@ +//combine.js-module to add a combine chord line +if(typeof abc2svg=="undefined") +var abc2svg={} +abc2svg.combine={comb_v:function(){var C=abc2svg.C,abc=this +function get_cmb(s){var p,s2=s.ts_next,i=s.p_v.id.indexOf('.') +if(i>=0){p=s.p_v.id.slice(0,i) +while(s2&&s2.time==s.time){if(s2.p_v.id.indexOf(p)==0) +break +s2=s2.ts_next}} +return s2} +function may_combine(s){var nhd2,s2=get_cmb(s) +if(!s2||(s2.type!=C.NOTE&&s2.type!=C.REST)) +return false +if(s2.st!=s.st||s2.time!=s.time||s2.dur!=s.dur) +return false +if(s.combine<=0&&s2.type!=s.type) +return false +if(s.a_gch&&s2.a_gch) +return false +if(s.type==C.REST){if(s.type==s2.type&&s.invis&&!s2.invis) +return false +return true} +if(s2.a_ly) +return false +if(s2.beam_st!=s.beam_st||s2.beam_end!=s.beam_end) +return false;nhd2=s2.nhd +if(s.combine<=1&&s.notes[0].pit<=s2.notes[nhd2].pit+1) +return false +return true} +function combine_notes(s,s2){var nhd,type,m,not +for(m=0;m<=s2.nhd;m++){not=abc.clone(s2.notes[m]) +not.noplay=true +s.notes.push(not)} +s.nhd=nhd=s.notes.length-1;s.notes.sort(abc2svg.pitcmp) +if(s.combine>=3){for(m=nhd;m>0;m--){if(s.notes[m].pit==s.notes[m-1].pit&&s.notes[m].acc==s.notes[m-1].acc) +s.notes.splice(m,1)} +s.nhd=nhd=s.notes.length-1} +s.ymx=3*(s.notes[nhd].pit-18)+4;s.ymn=3*(s.notes[0].pit-18)-4;type=s.notes[0].tie_ty +if((type&0x07)==C.SL_AUTO) +s.notes[0].tie_ty=C.SL_BELOW|(type&C.SL_DOTTED);type=s.notes[nhd].tie_ty +if((type&0x07)==C.SL_AUTO) +s.notes[nhd].tie_ty=C.SL_ABOVE|(type&C.SL_DOTTED)} +function do_combine(s){var s2,s3,type,i,n,sl +s2=get_cmb(s) +if(!s.in_tuplet&&s2.combine!=undefined&&s2.combine>=0&&may_combine(s2)) +s2=do_combine(s2) +if(s.type!=s2.type){if(s2.type!=C.REST){s2=s;s=s2.ts_next}}else if(s.type==C.REST){if(s.invis&&!s2.invis) +delete s.invis}else{combine_notes(s,s2) +if(s2.ti1) +s.ti1=true +if(s2.ti2) +s.ti2=true} +if(s2.sls){if(s.sls) +Array.prototype.push.apply(s.sls,s2.sls) +else +s.sls=s2.sls +for(i=0;i\n\ +\n\ +\n\ +' +glyphs.nut4=''}else{glyphs.fb6='\n\ +\n\ +\n\ +' +glyphs.nut6=''}} +function ch_cnv(t){var a=t.match(/[A-G][#♯b♭]?([^/]*)\/?/) +if(a&&a[1]){a[2]=abc2svg.ch_alias[a[1]] +if(a[2]!=undefined) +t=t.replace(a[1],a[2])} +return t.replace('/','.')} +function diag_add(nm){var dc,i,l,x,y,d=abc2svg.diag["cd"+n][nm.slice(1)] +if(!d) +return +d=d.split(' ') +x=2-2*n +dc='\n\ +\n' +l=d[1].split(',') +if(!l[0]||l[0].slice(-1)==l[1]) +dc+='\n' +if(l[0]) +dc+=''+l[0]+'\n' +dc+='' ++d[2].replace(/[y0]/g,' ') ++'\n' +for(i=0;i\n'} +if(d[3]){l=d[3].match(/barre=(\d)-(\d)/) +if(l) +dc+=''} +dc+='' +glyphs[nm]=dc} +of(i,s,x,y) +if((s.invis&&s.play)||!n) +return +gch=s.a_gch[i] +if(!gch||gch.type!='g'||gch.capo) +return +nm=n+ch_cnv(gch.text) +if(!glyphs[nm]) +diag_add(nm) +if(glyphs[nm]){x=s.x +y=abc.y_get(s.st,1,x-10,20) +abc.xygl(x,y+2,nm) +abc.y_set(s.st,1,x-10,20,y+34)}},set_fmt:function(of,cmd,param){var a,d,n,cfmt=this.cfmt() +switch(cmd){case"diagram":n=param +if(n=='0') +n='' +else if(n!='4') +n='6' +cfmt.diag=n +return +case"setdiag":n=cfmt.diag +if(!n) +n="6" +a=param.match(/(\S*)\s+(.*)/) +if(a&&a.length==3){d=a[2].split(' ') +if(d&&d.length>=3){abc2svg.diag["cd"+n][a[1].replace('/','.')]=a[2] +return}} +this.syntax(1,this.errs.bad_val,"%%setdiag") +return} +of(cmd,param)},set_hooks:function(abc){abc.draw_gchord=abc2svg.diag.draw_gchord.bind(abc,abc.draw_gchord) +abc.set_format=abc2svg.diag.set_fmt.bind(abc,abc.set_format)}} +if(!abc2svg.mhooks) +abc2svg.mhooks={} +abc2svg.mhooks.diag=abc2svg.diag.set_hooks diff --git a/lib/ChordPro/res/abc/abc2svg/edit-1.js b/lib/ChordPro/res/abc/abc2svg/edit-1.js new file mode 100644 index 00000000..42929e00 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/edit-1.js @@ -0,0 +1,930 @@ +// edit.js - file used in the abc2svg editor +// +// Copyright (C) 2014-2023 Jean-Francois Moine +// +// This file is part of abc2svg. +// +// abc2svg is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// abc2svg is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with abc2svg. If not, see . + +window.onerror = function(msg, url, line) { + if (typeof msg == 'string') + alert("window error: " + msg + + "\nURL: " + url + + "\nLine: " + line) + else if (typeof msg == 'object') + alert("window error: " + msg.type + ' ' + msg.target.src) + else + alert("window error: " + msg) + return false +} + +// function called before the page is closed +window.onbeforeunload = function () { + if (chg) // if the textarea has been modified + return "" // ask close page confirmation +} + +var abc_images, // image buffer + abc_fname = ["noname.abc", ""], // file names + abc_mtime = [], // associated last modification time + abc, // Abc object + syms, // music symbol at source index + ctxMenu, // context menu for play + elt_ref = {}, // pointers to page HTML elements + selx = [0, 0], // selected source indexes + selx_sav = [], // (saved while playing/printing) + play = {}, // play data + pop, // current popup message + texts = {}, // language specific texts + chg = 0, // > 0 when the textarea is modified + jsdir = document.currentScript ? + document.currentScript.src.match(/.*\//) : + (function() { + var s_a = document.getElementsByTagName('script') + for (var k = 0; k < s_a.length; k++) { + if (s_a[k].src.indexOf('edit-') >= 0) + return s_a[k].src.match(/.*\//) || '' + } + return "" // ?? + })() + +// -- Abc create argument +var user = { + // -- required methods + // include a file (%%abc-include - only one) + read_file: function(fn) { + elt_ref["s" + srcidx].style.display = "inline" + return elt_ref.src1.value + }, + // insert the errors + errbld: function(sev, txt, fn, idx) { + var msg = sev + ' ' + clean_txt(txt) + if (idx >= 0) + elt_ref.diverr.innerHTML += '' + + msg + "
\n" + else + elt_ref.diverr.innerHTML += msg + "
\n" + }, + // image output + img_out: function(str) { + abc_images += str + }, + // -- optional methods + // annotations + anno_stop: function(type, start, stop, x, y, w, h, s) { + if (["beam", "slur", "tuplet"].indexOf(type) >= 0) + return + syms[start] = s // music symbol + + // create a rectangle + abc.out_svg('\n') +// with absolute coordinates, the rectangles would be inserted at the end +// of the images as: +// '\n' + }, + // -- optional attributes + page_format: true // define the non-page-breakable blocks + }, + srcidx = 0 + +// -- local functions + +// Storage handling +function storage(t, // session or local + k, v) { + try { + t = t ? localStorage : sessionStorage + if (!t) + return + if (v) + t.setItem(k, v) + else if (v === 0) + t.removeItem(k) + else + return t.getItem(k) + } catch(e) { + } +} + +// replace <>& by XML character references +function clean_txt(txt) { + return txt.replace(/<|>|&.*?;|&/g, function(c) { + switch (c) { + case '<': return "<" + case '>': return ">" + case '&': return "&" + } + return c + }) +} + +// load the language files ('edit-lang.js' and 'err-lang.js') +function loadlang(lang, no_memo) { + abc2svg.loadjs('edit-' + lang + '.js', function() { loadtxt() }); + abc2svg.loadjs('err-' + lang + '.js') + if (!no_memo) + storage(true, "lang", lang == "en" ? 0 : lang) +} + +// show/hide a popup message +function popshow(area, visible) { + var e = document.getElementById(area) + if (pop) { + if (pop == e) + visible = false + else + pop.style.visibility = 'hidden' + } + e.style.visibility = visible ? 'visible' : 'hidden'; + pop = visible ? e : null +} + +// load the (ABC source or include) file in the textarea +function loadtune() { + var files = document.getElementById("abcfile").files +// if (!files.length) { +// alert('Please select a file!') +// return +// } + abc_fname[srcidx] = files[0].name + + var reader = new FileReader(); + + // Closure to capture the file information + reader.onloadend = function(evt) { + var s = srcidx == 0 ? "source" : "src1"; + + elt_ref[s].value = evt.target.result; + elt_ref["s" + srcidx].value = abc_fname[srcidx]; + chg = -1 // no change yet + src_change() + } + + // Read the file as text + reader.readAsText(files[0], "UTF-8") +} + +// display the source 0 or 1 +function selsrc(idx) { + if (idx == srcidx) + return + var o = srcidx ? "src" + srcidx : "source", + n = idx ? "src" + idx : "source"; + elt_ref[o].style.display = "none"; + elt_ref[n].style.display = "inline"; + elt_ref["s" + srcidx].style.backgroundColor = "#ffd0d0"; + elt_ref["s" + idx].style.backgroundColor = "#80ff80"; + srcidx = idx +} + +// render the textarea content to the right side +function render() { + var i, j, + content = elt_ref.source.value; + + if (!content) + return // empty source + + // if include file not loaded yet, ask it + i = content.indexOf('%%abc-include ') + if (i >= 0) { + var sl = elt_ref.s1 + if (!sl.value) { + sl.style.display = "inline"; + j = content.indexOf('\n', i); + sl.value = content.slice(i + 14, j); + selsrc(1); + alert(texts.load + sl.value) + return + } + } + elt_ref.diverr.innerHTML = ''; + selx[0] = selx[1] = 0; + chg++ // textarea modified + render2() +} +function render2() { + var content = elt_ref.source.value, + def = (abc2svg.a_inc && abc2svg.a_inc["default.abc"]) || '' + + // load the required modules + if (!abc2svg.modules.load(content + elt_ref.src1.value + def, render2)) + return + + // if page formatting, define a function to get the modification date + if (abc2svg.modules.pageheight.loaded) { + abc2svg.get_mtime = function(fn) { + var files = document.getElementById("abcfile").files + if (files && files[0].lastModified) + return new Date(files[0].lastModified) + return new Date() + } + } + + abc = new abc2svg.Abc(user); + abc_images = ''; + +// document.body.style.cursor = "wait"; + syms = [] + + // insert the file default.abc if loaded + if (abc2svg.a_inc && abc2svg.a_inc["default.abc"]) { + try { + abc.tosvg("default.abc", abc2svg.a_inc["default.abc"]) + } catch(e) { + alert(e.message + '\nabc2svg tosvg bug - stack:\n' + e.stack) + return + } + } + + // render the textarea + try { + abc.tosvg(abc_fname[0], content) + } catch(e) { + alert(e.message + '\nabc2svg tosvg bug - stack:\n' + e.stack) + return + } + abc2svg.abc_end() // close the page if %%pageheight + +// document.body.style.cursor = "auto"; + + try { + elt_ref.target.innerHTML = abc_images + } catch(e) { + alert(e.message + '\nabc2svg image bug - abort') + return + } + + // show the 'Error' button if some error + document.getElementById("er").style.display = + elt_ref.diverr.innerHTML ? 'inline' : 'none'; +} + +// get the source offset from the row and column numbers +function soffs(r, c) { + var m, + s = elt_ref.source, + o = 0 + while (--r >= 0) { + o = s.value.indexOf('\n', o) + 1 + if (o <= 0) + return s.value.length - 1 + } + m = s.value.indexOf('\n', o) // check if inside the line + o += c + if (o > m) + o = m + return o +} // soffs() + +// select a source ABC element on error +function gotoabc(l, c) { + var s = elt_ref.source + + selsrc(0) + if (l >= 0) + c = soffs(l, Number(c)) + s.blur() // unfocus + s.setSelectionRange(c, c) // just set the cursor (no range) + s.focus() // should scroll the textarea + s.setSelectionRange(c, syms[c] ? syms[c].iend : c + 1) +} + +// click in the target +function selsvg(evt) { + var v, + cl = evt.target.getAttribute('class') + + play.loop = false; + +// evt.stopImmediatePropagation(); +// evt.preventDefault() + + // remove the context menu if active + if (ctxMenu && ctxMenu.style.display == "block") { + ctxMenu.style.display = "none" + return + } + + // stop playing + if (play.playing && !play.stop) { + play.stop = -1; + play.abcplay.stop() + return + } + + // highlight the clicked element or clear the selection start + s = elt_ref.source; + s.blur(); + v = cl && cl.substr(0, 4) == 'abcr' ? Number(cl.slice(6, -1)) : 0 + s.setSelectionRange(v, v) // just a cursor + s.focus() + if (v) + s.setSelectionRange(v, syms[v].iend) +} + +// set/clear a selection +function setsel(idx, v) { + var i, elts, s, + old_v = selx[idx]; + + if (v == old_v) + return + if (old_v) { + elts = document.getElementsByClassName('_' + old_v + '_'); + i = elts.length + while (--i >= 0) + elts[i].style.fillOpacity = 0 + } + if (v) { + elts = document.getElementsByClassName('_' + v + '_'); + i = elts.length + while (--i >= 0) + elts[i].style.fillOpacity = 0.4 + } + + selx[idx] = v +} + +function do_scroll(elt) { + var x = 0, + y = 0, + b = elt.getBoundingClientRect(), // box of the rectangle + d = elt_ref.target.parentElement, //
'dright' + r = elt.parentNode.getBoundingClientRect() // box of the svg container + + if (b.x < d.offsetLeft // x offset of the rectangle + || b.x + b.width > d.offsetLeft + d.clientWidth * .7) + x = b.x - d.offsetLeft - d.clientWidth * .3 + + if (r.y < d.offsetTop // y offset of the svg container + || r.y + r.height > d.offsetTop + d.clientHeight * .7) + y = r.y - d.offsetTop - d.clientHeight * .3 + + if (x || y) +// d.scrollBy(x, y) + d.scrollBy({ + top: y, + left: x, + behavior: (x < 0 || y) ? 'instant' : 'smooth' + // 'smooth', 'instant' or 'auto' (default) + }) +} + +// source text selection callback +function seltxt(evt) { + var s, elts, + e = 0, + elt = elt_ref.source, + start = elt.selectionStart, + end = elt.selectionEnd + + play.loop = false + + if (!start) { + if (end == elt.value.length) + return // select all + setsel(0, 0); // clear selection + setsel(1, 0) + return + } + + if (syms) { + syms.forEach(function(sym, is) { + if (!s) { + if (is >= start) + s = is + } else if (sym.iend <= end) { + e = is + } + }) + } + if (!s) + return + if (selx[0] != s) + setsel(0, s) + if (selx[1] != e) + setsel(1, e); + elts = document.getElementsByClassName('_' + s + '_') + if (elts[0]) + do_scroll(elts[0]) // move the element in the screen +} + +// open a new window for file save +function saveas() { + var s = srcidx == 0 ? "source" : "src1", + source = elt_ref[s].value, + + // create a link for our script to 'click' + link = document.createElement("a"); + + if (abc_fname[srcidx] == "noname.abc") + elt_ref["s" + srcidx].value = + abc_fname[srcidx] = prompt(texts.fn, abc_fname[srcidx]) + link.download = abc_fname[srcidx]; +// link.innerHTML = "Hidden Link"; + link.href = "data:text/plain;charset=utf-8," + + encodeURIComponent(source); + + // open in a new tab +// link.target = '_blank'; + + // when link is clicked call a function to remove it from + // the DOM in case user wants to save a second file. + link.onclick = destroyClickedElement; + + // make sure the link is hidden. + link.style.display = "none"; + + // add the link to the DOM + document.body.appendChild(link); + + // click the new link + link.click() + + chg = 0 // no change anymore +} + +// destroy the clicked element +function destroyClickedElement(evt) { + document.body.removeChild(evt.target) +} + +// set the size of the font of the textarea +function setfont() { + var fs = document.getElementById("fontsize").value.toString(); + elt_ref.source.style.fontSize = + elt_ref.src1.style.fontSize = fs + "px"; + storage(true, "fontsz", fs == "14" ? 0 : fs) +} + +// playing +// set soundfont URL +function set_sfu(v) { + play.abcplay.set_sfu(v) + storage(true, "sfu", v == "Scc1t2" ? 0 : v) +} +// set_speed value = 1..20, 10 = no change +function set_speed(iv) { + var spvl = document.getElementById("spvl"), + v = Math.pow(3, // max 3 times lower/faster + (iv - 10) * .1); + play.abcplay.set_speed(v); + spvl.innerHTML = v +} +// set volume +function set_vol(v) { + var gvl = document.getElementById("gvl"); + gvl.innerHTML = v.toFixed(2); + play.abcplay.set_vol(v) + storage(true, "volume", v == 0.7 ? 0 : v.toFixed(2)) +} +function notehlight(i, on) { + if (play.stop) { + if (on) // (should not occur anymore) + return + if (play.stop < 0) // if first stop + play.stop = i // keep the last note reference + if (i == selx[1]) // if end selection + return // don't remove highlight + } + var elts = document.getElementsByClassName('_' + i + '_'); + if (elts && elts[0]) { + if (on) + do_scroll(elts[0]); + elts[0].style.fillOpacity = on ? 0.4 : 0 + } +} +function endplay(repv) { + if (play.loop) { + play.abcplay.play(play.si, play.ei) + return + } + play.playing = false; + play.repv = repv // repeat variant number for continue + + // redisplay the selection + selx[0] = selx[1] = 0; + setsel(0, selx_sav[0]); + setsel(1, selx_sav[1]) +} + +// start playing +// -1: All (removed) +// 0: Tune +// 1: Selection +// 2: Loop +// 3: Continue +function play_tune(what) { + if (!abc) + return // no generation yet + var i, si, ei, elt, + C = abc2svg.C, + tunes = abc.tunes + + if (play.playing) { + if (!play.stop) { + play.stop = -1; + play.abcplay.stop() + } + return + } + + // search a symbol to play + function gnrn(sym, loop) { // go to the next real note (not tied) + var i + while (1) { + switch (sym.type) { + case C.NOTE: + i = sym.nhd + 1 + while (--i >= 0) { + if (sym.notes[i].ti2) + break + } + if (i < 0) + return sym + break + case C.REST: + case C.GRACE: + return sym + case C.BLOCK: + switch (sym.subtype) { + case "midictl": + case "midiprog": + return sym + } + break + } + if (!sym.ts_next) { + if (!loop) + return gprn(sym, 1) + return sym + } + sym = sym.ts_next + } + // not reached + } + + function gprn(sym, loop) { // go to the previous real note (not tied) + var i + while (1) { + switch (sym.type) { +// case C.BAR: +//fixme: there may be a right repeat! +// break + case C.NOTE: + i = sym.nhd + 1 + while (--i >= 0) { + if (sym.notes[i].ti2) + break + } + if (i < 0) + return sym + break + case C.REST: + case C.GRACE: + return sym + case C.BLOCK: + switch (sym.subtype) { + case "midictl": + case "midiprog": + return sym + } + break + } + if (!sym.ts_prev) { + if (!loop) + return gnrn(sym, 1) + return sym + } + sym = sym.ts_prev + } + // not reached + } + + function gsot(si) { // go to the first symbol of a tune + var sym = syms[si].p_v.sym + + while (!sym.seqst) + sym = sym.ts_prev + return sym + } + function get_se(si) { // get the starting symbol + var sym = syms[si] + + while (!sym.seqst) + sym = sym.ts_prev + return sym + } // get_se() + + function get_ee(si) { // get the ending symbol + var sym = syms[si] + + while (sym.ts_next && !sym.ts_next.seqst) + sym = sym.ts_next + return sym + } // get_ee() + + // start playing + function play_start(si, ei) { + if (!si) + return + selx_sav[0] = selx[0]; // remove the colors + selx_sav[1] = selx[1]; + setsel(0, 0); + setsel(1, 0); + + play.stop = 0; + play.abcplay.play(si, ei, play.repv) + } + + // play tune() + ctxMenu.style.display = "none"; // remove the play menu + + play.playing = true; + if (tunes.length) { // if new display + + // generate the play data of all tunes + while (1) { + elt = tunes.shift() + if (!elt) + break + play.abcplay.add(elt[0], elt[1], elt[3]) + } + + play.si = play.ei = null + play.stop = 0 + play.loop = false + } + + // if loop again + if (what == 2 && play.loop) { + play_start(play.si, play.ei) + return + } + + // get the starting and ending play indexes, and start playing + + if (what == 3 && play.stop > 0) { // if stopped and continue + play_start(get_se(play.stop), play.ei) + return + } + if (what != 0 && selx[0] && selx[1]) { // if full selection + si = get_se(selx[0]); + ei = get_ee(selx[1]) + } else if (what != 0 && selx[0]) { // if selection without end + si = get_se(selx[0]); + ei = null + } else if (what != 0 && selx[1]) { // if selection without start + si = gsot(selx[1]) + ei = get_ee(selx[1]) + } else { // no selection => tune + elt = play.click.svg // (dummy) + si = elt.getElementsByClassName('abcr') // symbols in the SVG + if (!si.length) { + play.playing = false + return // not in a tune! + } + i = Number(si[0].getAttribute('class').slice(6, -1)) + si = gsot(i) + ei = null + } + + if (what != 3) { // if not continue + play.si = si; + play.ei = ei; + play.loop = what == 2 + play.repv = 0 + } + + play_start(si, ei) +} + +// set the version and initialize the playing engine +function edit_init() { + var a, i, e + + // loop until abc2svg is fully loaded + if (typeof abc2svg != "object" + || !abc2svg.modules) { + setTimeout(edit_init, 500) + return + } + +// function to load javascript files + abc2svg.loadjs = function(fn, relay, onerror) { + var s = document.createElement('script'); + if (/:\/\//.test(fn)) + s.src = fn // absolute URL + else + s.src = jsdir + fn; + s.type = 'text/javascript' + if (relay) + s.onload = relay; + s.onerror = onerror || function() { + alert('error loading ' + fn) + } + document.head.appendChild(s) + } + + // accept page formatting + abc2svg.abc_end = function() {} + + function set_pref() { + var v = storage(true, "fontsz") + if (v) { + elt_ref.source.style.fontSize = + elt_ref.src1.style.fontSize = + v + "px"; + document.getElementById("fontsize").value = + Number(v) + } + + // set the language + // if not defined, get the one of the navigator + v = storage(true, "lang"); + if (!v) { + v = (navigator.languages ? + navigator.languages[0] : + navigator.language).split('-')[0] + switch (v) { + case "de": + case "en": + case "fr": + case "it": break + case "pt": v = "pt_BR"; break + default: v = "en"; break + } + } + loadlang(v, true) + } + + document.getElementById("abc2svg").innerHTML = + 'abc2svg-' + abc2svg.version + ' (' + abc2svg.vdate + ')' + + // keep references on the page elements + a = ["diverr", "source", "src1", "s0", "s1", "target"] + for (i = 0; i < a.length; i++) { + e = a[i] + elt_ref[e] = document.getElementById(e) + } + + // set the callback functions + document.getElementById("saveas").onclick = saveas; + elt_ref.s0.onclick = function(){selsrc(0)}; + elt_ref.s1.onclick = function(){selsrc(1)}; + elt_ref.target.onclick = selsvg; + elt_ref.source.onselect = seltxt; + + // remove the selection on print + window.onbeforeprint = function() { + selx_sav[0] = selx[0]; // remove the colors + selx_sav[1] = selx[1]; + setsel(0, 0); + setsel(1, 0) + }; + window.onafterprint = function() { + setsel(0, selx_sav[0]); + setsel(1, selx_sav[1]) + } + + // if playing is possible, load the playing script + if (window.AudioContext || window.webkitAudioContext + || navigator.requestMIDIAccess) { + abc2svg.loadjs("snd-1.js", function() { + play.abcplay = AbcPlay({ + onend: endplay, + onnote:notehlight, + }); + document.getElementById("playdiv1").style.display = + document.getElementById("playdiv3").style.display = + document.getElementById("playdiv4").style.display = + "list-item"; + + document.getElementById("sfu").value = play.abcplay.set_sfu(); +// document.getElementById("spv").innerHTML = +// Math.log(play.abcplay.set_speed()) / Math.log(3); + document.getElementById("gvol").setAttribute("value", + play.abcplay.set_vol() * 10) + document.getElementById("gvl").setAttribute("value", + (play.abcplay.set_vol() * 10).toFixed(2)) + }); + + // display the play menu + // This function is called on non left click + function show_menu(evt) { + var x, y, + elt = evt.target, + cl = elt.getAttribute('class') + + // if right click on an element, select it + if (cl && cl.substr(0, 4) == 'abcr') { + setsel(1, Number(cl.slice(6, -1))) + evt.preventDefault() + return + } + + // if the play menu is already active + // display the default context menu + ctxMenu = document.getElementById("ctxMenu"); + if (ctxMenu.style.display == "block") + return + + // otherwise, display the play menu + evt.preventDefault() + play.click = { // keep the click references for 'play tune' + svg: elt, + Y: evt.pageY + } + + ctxMenu.style.display = "block"; + x = evt.pageX - elt_ref.target.parentNode.offsetLeft + + elt_ref.target.parentNode.scrollLeft; + y = evt.pageY + elt_ref.target.parentNode.scrollTop; + ctxMenu.style.left = (x - 30) + "px"; + ctxMenu.style.top = (y - 10) + "px" + } // showmenu + + e = elt_ref.target +// e.onauxclick = show_menu // right or middle buttons (KO in Safari) + e.ondblclick = show_menu // double click + e.oncontextmenu = show_menu + } + set_pref() // set the preferences from local storage +} + +// drag and drop +function drag_enter(evt) { + evt.stopImmediatePropagation(); + evt.preventDefault() // allow drop +//fixme: "move" does not work +// evt.dataTransfer.dropEffect = evt.ctrlKey ? "move" : "copy" +} +function drop(evt) { + evt.stopImmediatePropagation(); + evt.preventDefault() + // check if text + var data = evt.dataTransfer.getData("text") + if (data) { // insert the text at the mouse location + var x = evt.layerX, + y = elt_ref.source.scrollTop + evt.layerY, + h = elt_ref.source.offsetHeight / elt_ref.source.rows, + w = elt_ref.source.offsetWidth / elt_ref.source.cols + + w = (x / w) | 0 // column + h = (y / h) | 0 // row + h = soffs(h, w) + + var e = evt.target + e.value = e.value.slice(0, h) + + data + + e.value.slice(h) + src_change() + return + } + // check if file + data = evt.dataTransfer.files // FileList object. + if (data.length) { + var reader = new FileReader(), + s = srcidx == 0 ? "source" : "src1" + + elt_ref["s" + srcidx].value = abc_fname[srcidx] = data[0].name + reader.onload = function(evt) { + elt_ref[s].value = evt.target.result + chg = -1 // no change yet + src_change() + } + reader.readAsText(data[0],"UTF-8") +// return + } +} + +// render the music 2 seconds after source change +var timer +function src_change() { + clearTimeout(timer); + if (!play.playing) + timer = setTimeout(render, 2000) +} + +// wait for scripts to be loaded +window.addEventListener("load", edit_init) diff --git a/lib/ChordPro/res/abc/abc2svg/equalbars-1.js b/lib/ChordPro/res/abc/abc2svg/equalbars-1.js new file mode 100644 index 00000000..696787e7 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/equalbars-1.js @@ -0,0 +1,55 @@ +//equalbars.js-module to set equal spaced measure bars +if(typeof abc2svg=="undefined") +var abc2svg={} +abc2svg.equalbars={output_music:function(of){this.equalbars_d=0;of()},set_fmt:function(of,cmd,parm){if(cmd!="equalbars"){of(cmd,parm) +return} +var fmt=this.cfmt() +fmt.equalbars=this.get_bool(parm) +fmt.stretchlast=1},set_sym_glue:function(of,width){var C=abc2svg.C,s,s2,d,w,i,n,x,g,t,t0,bars=[],tsfirst=this.get_tsfirst();of(width) +if(!this.cfmt().equalbars) +return +for(s2=tsfirst;s2;s2=s2.next){switch(s2.type){default:continue +case C.GRACE:case C.MREST:case C.NOTE:case C.REST:case C.SPACE:break} +break} +if(!s2) +return +t0=t=s2.time +for(s=s2;s.next;s=s.next){if(s.type==C.BAR&&s.seqst&&s.time!=t){bars.push([s,s.time-t]);t=s.time}} +if(s.time!=t) +bars.push([s,s.time-t]) +else +bars[bars.length-1][0]=s +t=s.time +if(s.dur) +t+=s.dur;n=bars.length +if(n<=1) +return +if(s.xw) +w=s.x-x +x=s.x} +if(w*n=C.BLEN) +n=3 +else if(s.dur==C.BLEN/2) +n=1 +else +n=2 +d=d_orig=C.BLEN/4 +s.dur=s.dur_orig=C.BLEN/4} +for(m=0;m<=s.nhd;m++) +s.notes[m].dur=s.dur +s.beam_on=true +while(--n>=0){s2={type:C.REST,v:s.v,p_v:s.p_v,st:s.st,fmt:s.fmt,dur:d,dur_orig:d_orig,stem:1,multi:0,nhd:0,notes:[{pit:s.notes[0].pit,jn:8}],xmx:0,beam_on:true,noplay:true,time:s.time+s.dur,prev:s,next:s.next} +s.next=s2 +if(s2.next) +s2.next.prev=s2 +if(!s.ts_next){s.ts_next=s2 +if(s.soln) +s.soln=false +s2.ts_prev=s +s2.seqst=true}else{for(s3=s.ts_next;s3;s3=s3.ts_next){if(s3.times2.time){s2.seqst=true +s3=s3.ts_prev} +s2.ts_next=s3.ts_next +s2.ts_prev=s3 +if(s2.ts_next) +s2.ts_next.ts_prev=s2 +s3.ts_next=s2 +break}} +s=s2}} +function do_tie(s){var end_time=s.time+s.dur +while(1){s=s.ts_next +if(!s||s.time>end_time) +return +if(s.type==C.NOTE&&s.time==end_time) +break} +s.notes[0].jn=8 +s.notes[0].jo=2} +function set_sym(p_v){var s,s2,note,pit,nn,p,a,m,i,sf=p_v.key.k_sf,delta=abc2svg.gamelan.cgd2cde[sf+7]-2 +delete p_v.key.k_a_acc +p_v.clef.invis=true +for(s=p_v.sym;s;s=s.next){s.st=p_v.st +switch(s.type){case C.CLEF:s.invis=true +default:continue +case C.KEY:delta=abc2svg.gamelan.cgd2cde[s.k_sf+7]-2 +continue +case C.REST:if(s.notes[0].jn) +continue +s.notes[0].jn=0 +s.notes[0].pit=21 +slice(s) +continue +case C.NOTE:break} +s.stem=1 +s.stemless=true +if(s.sls){for(i=0;i\n') +y-=2.5 +while(++n<=nl){s=s1 +while(1){if(s.nflags&&s.nflags>=n){s3=s +while(s!=s2){if(s.next.beam_br1||(s.next.beam_br2&&n>2)||(s.next.nflags&&s.next.nflags'+p+'\n')} +function out_txt(x,y,p){out_svg(''+p+'\n')} +function draw_hd(s,x,y){var m,note,ym +for(m=0;m<=s.nhd;m++){note=s.notes[m] +out_txt(x-3.5,y+8,"01234567."[note.jn]) +if(note.acc){out_svg('\n')} +if(note.jo>2){out_mus(x-1,y+23,dot) +if(note.jo>3){y+=3 +out_mus(x-1,y+23.4,dot)}}else if(note.jo<2){ym=y+4 +out_mus(x-1,ym,dot)} +y+=20}} +for(s=p_voice.sym;s;s=s.next){if(s.invis) +continue +switch(s.type){case C.NOTE:case C.REST:x=s.x +y=staff_tb[s.st].y +draw_hd(s,x,y) +break}} +for(s=p_voice.sym;s;s=s.next){if(s.invis) +continue +switch(s.type){case C.NOTE:case C.REST:nl=s.nflags +if(nl<=0) +continue +y=staff_tb[s.st].y +s2=s +while(s.next&&s.next.nflags>0){s=s.next +if(s.nflags>nl) +nl=s.nflags +if(s.beam_end) +break} +draw_dur(s2,y+7,s,1,nl) +break}}},set_fmt:function(of,cmd,param){if(cmd=="gamelan"){var cfmt=this.cfmt() +if(!this.get_bool(param)) +return +cfmt.gamelan=true +cfmt.staffsep=20 +cfmt.sysstaffsep=14 +this.set_v_param("stafflines","...") +cfmt.tuplets=[0,1,0,1] +return} +of(cmd,param)},set_pitch:function(of,last_s){of(last_s) +if(!last_s||!this.cfmt().gamelan) +return +var C=abc2svg.C +for(var s=this.get_tsfirst();s;s=s.ts_next){switch(s.type){case C.NOTE:s.ymx=20*s.nhd+(s.nflags>0?30:24) +if(s.notes[s.nhd].jo>2){s.ymx+=3 +if(s.notes[s.nhd].jo>3) +s.ymx+=2} +s.ys=s.ymx +break}}},set_width:function(of,s){of(s) +if(!this.cfmt().gamelan) +return +var w,m,note,C=abc2svg.C +switch(s.type){case C.CLEF:case C.KEY:case C.METER:s.wl=s.wr=.1 +break}},set_hooks:function(abc){abc.do_pscom=abc2svg.gamelan.do_pscom.bind(abc,abc.do_pscom) +abc.draw_symbols=abc2svg.gamelan.draw_symbols.bind(abc,abc.draw_symbols) +abc.output_music=abc2svg.gamelan.output_music.bind(abc,abc.output_music) +abc.set_format=abc2svg.gamelan.set_fmt.bind(abc,abc.set_format) +abc.set_pitch=abc2svg.gamelan.set_pitch.bind(abc,abc.set_pitch) +abc.set_width=abc2svg.gamelan.set_width.bind(abc,abc.set_width) +abc.get_glyphs().gstc='' +abc.get_decos().gstc="0 gstc 5 1 1" +abc.add_style("\n.bn {font-family:sans-serif; font-size:16px}")}} +if(!abc2svg.mhooks) +abc2svg.mhooks={} +abc2svg.mhooks.gamelan=abc2svg.gamelan.set_hooks diff --git a/lib/ChordPro/res/abc/abc2svg/grid-1.js b/lib/ChordPro/res/abc/abc2svg/grid-1.js new file mode 100644 index 00000000..b2d61bb7 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/grid-1.js @@ -0,0 +1,287 @@ +//abc2svg-grid.js-module to insert a chord grid before or after a tune +if(typeof abc2svg=="undefined") +var abc2svg={} +abc2svg.grid={pl:'1){abc.out_svg(abc2svg.grid.pl) +abc.out_sxsy(x-wmx/2,' ',yl) +abc.out_svg('l'+ +wmx.toFixed(1)+' -'+hr.toFixed(1)+'"/>\n') +if(cell[1]){abc.out_svg(abc2svg.grid.pl) +abc.out_sxsy(x-wmx/2,' ',yl+hr) +abc.out_svg('l'+ +(wmx/2).toFixed(1)+' '+(hr/2).toFixed(1)+'"/>\n') +abc.set_font('gs') +abc.xy_str(x-wmx/3,y,cell[0]) +abc.xy_str(x,y+hr/3,cell[1])}else{abc.set_font('gs') +abc.xy_str(x-wmx*.2,y+hr/4,cell[0])} +if(cell.length>=3){if(cell[3]){abc.out_svg(abc2svg.grid.pl) +abc.out_sxsy(x,' ',yl+hr/2) +abc.out_svg('l'+ +(wmx/2).toFixed(1)+' '+(hr/2).toFixed(1)+'"/>\n') +abc.set_font('gs') +abc.xy_str(x,y-hr/3,cell[2]) +abc.xy_str(x+wmx/3,y,cell[3])}else{abc.set_font('gs') +abc.xy_str(x+wmx*.2,y-hr/4,cell[2])}}}else{abc.set_font('grid') +abc.xy_str(x,y,cell[0])}} +function draw_hl(){var i,i1,j,x,y=-1 +for(i=0;i<=nr+1;i++){j=0 +i1=i>0?i-1:0 +while(1){while(j<=nc&&!d[i1][j]) +j++ +if(j>nc) +break +x=wmx*j +while(j<=nc&&d[i1][j]) +j++ +if(i&&i10?i-1:0 +while(1){while(j<=nr&&!d[j][i1]) +j++ +if(j>nr) +break +y=hr*j +while(j<=nr&&d[j][i1]) +j++ +abc.out_svg('M') +abc.out_sxsy(x,' ',-y-.5) +abc.out_svg('v'+(hr*j-y+1).toFixed(1)+'\n')} +x+=wmx}} +set_chords() +if(!grid.ls){cells=chords}else{bar=bars;bars=[] +ps=parts +parts=[] +for(i=0;icells.length) +nc=cells.length;hr=font.size*2 +if(wmxx0){nc/=2;w/=2} +yl=-1 +y=-1+font.size*.6 +nr=-1 +x0=(x0/cfmt.scale-w)/2 +d=[] +for(i=0;i=nc){y-=hr +yl-=hr +x=x0+wmx/2 +k=0 +nr++ +d[nr]=[]} +d[nr][k]=1 +k++ +build_cell(cells[i],x,y,yl,hr) +x+=wmx} +abc.out_svg('\n') +y=-1+font.size*.7 +x=x0 +for(i=0;i:\n')} +if(i==0||(grid.repbrk&&(bar.slice(-1)==':'||bar[0]==':'))||parts[i]||k>=nc){y-=hr;x=x0 +k=0 +if(parts[i]){w=abc.strwh(parts[i])[0] +abc.out_svg(''+ +parts[i]+'\n')}} +k++ +if(bar.slice(-1)==':'){abc.out_svg(':\n')} +x+=wmx} +abc.vskip(hr*(nr+1)+6)} +var p_voice,n,font,f2 +abc.set_page() +img=abc.get_img() +font=abc.get_font('grid') +if(font.class) +font.class+=' mid' +else +font.class='mid' +cls=abc.font_class(font) +abc.param_set_font("gsfont",font.name+' '+(font.size*.7).toFixed(1)) +f2=cfmt.gsfont +if(font.weight) +f2.weight=font.weight +if(font.style) +f2.style=font.style +f2.class=font.class +abc.add_style("\n.mid {text-anchor:middle}") +abc.blk_flush() +build_grid(s,font) +abc.blk_flush()},set_stems:function(of){var C=abc2svg.C,abc=this,tsfirst=abc.get_tsfirst(),voice_tb=abc.get_voice_tb(),cfmt=abc.cfmt(),grid=cfmt.grid +function cs_filter(a_cs){var i,cs,t +for(i=0;icur_beat){if(beat_i<3) +beat_i++ +cur_beat+=beat} +if(s.part) +parts[chords.length]=s.part.text +switch(s.type){case C.NOTE:case C.REST:case C.SPACE:if(!s.a_gch||chord[beat_i]) +break +bt=cs_filter(s.a_gch) +if(!bt) +break +w=abc.strwh(bt.replace(/<[^>]+>/gm,'')) +if(w[0]>wmx) +wmx=w[0] +bt=new String(bt) +bt.wh=w +chord[beat_i]=bt +break +case C.BAR:i=s.bar_num +bt=s.bar_type +while(s.ts_next&&s.ts_next.time==s.time){if(s.ts_next.dur||s.ts_next.type==C.SPACE) +break +s=s.ts_next +if(s.type==C.METER){beat=get_beat(s) +wm=s.wmeasure +continue} +if(s.type!=C.BAR) +continue +if(s.bar_type[0]==':'&&bt[0]!=':') +bt=':'+bt +if(s.bar_type.slice(-1)==':'&&bt.slice(-1)!=':') +bt+=':' +if(s.bar_num) +i=s.bar_num +if(s.part) +parts[chords.length+1]=s.part.text} +if(grid.norep) +bt='|' +if(s.time=0) +rep=true +break +case C.METER:beat=get_beat(s) +wm=s.wmeasure +break}} +if(chord.length){bars.push('') +chords.push(chord)} +if(!some_chord) +return +wmx+=abc.strwh(rep?' ':' ')[0] +sb.chords=chords +sb.bars=bars +if(grid.parts&&parts.length) +sb.parts=parts +sb.wmx=wmx} +if(grid){var C=abc2svg.C,tsfirst=this.get_tsfirst(),fmt=tsfirst.fmt,voice_tb=this.get_voice_tb(),p_v=voice_tb[this.get_top_v()],s={type:C.BLOCK,subtype:'grid',dur:0,time:0,p_v:p_v,v:p_v.v,st:p_v.st} +if(!cfmt.gridfont) +abc.param_set_font("gridfont","serif 16") +abc.set_font('grid') +build_chords(s) +if(!s.chords){}else if(grid.nomusic){this.set_tsfirst(s)}else if(grid.n<0){for(var s2=tsfirst;s2.ts_next;s2=s2.ts_next);s.time=s2.time +s.prev=p_v.last_sym.prev +s.prev.next=s +s.next=p_v.last_sym +p_v.last_sym.prev=s +s.ts_prev=s2.ts_prev +s.ts_prev.ts_next=s +s.ts_next=s2 +s2.ts_prev=s +if(s2.seqst){s.seqst=true +s2.seqst=false}}else{s.next=p_v.sym +s.ts_next=tsfirst +tsfirst.ts_prev=s +this.set_tsfirst(s) +p_v.sym.prev=s +p_v.sym=s} +s.fmt=s.prev?s.prev.fmt:fmt} +of()},set_fmt:function(of,cmd,parm){if(cmd=="grid"){if(!parm) +parm="1";parm=parm.split(/\s+/) +var grid={n:Number(parm.shift())} +if(isNaN(grid.n)){if(parm.length){this.syntax(1,this.errs.bad_val,"%%grid") +return} +grid.n=1} +while(parm.length){var item=parm.shift() +if(item=="norepeat") +grid.norep=true +else if(item=="nomusic") +grid.nomusic=true +else if(item=="parts") +grid.parts=true +else if(item=="repbrk") +grid.repbrk=true +else if(item.slice(0,8)=="include=") +grid.ls=item.slice(8).split(',')} +this.cfmt().grid=grid +return} +of(cmd,parm)},set_hooks:function(abc){abc.block_gen=abc2svg.grid.block_gen.bind(abc,abc.block_gen) +abc.set_stems=abc2svg.grid.set_stems.bind(abc,abc.set_stems) +abc.set_format=abc2svg.grid.set_fmt.bind(abc,abc.set_format)}} +if(!abc2svg.mhooks) +abc2svg.mhooks={} +abc2svg.mhooks.grid=abc2svg.grid.set_hooks diff --git a/lib/ChordPro/res/abc/abc2svg/grid2-1.js b/lib/ChordPro/res/abc/abc2svg/grid2-1.js new file mode 100644 index 00000000..0fc049fb --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/grid2-1.js @@ -0,0 +1,46 @@ +//grid2.js-module to replace a voice in the music by a chord grid +if(typeof abc2svg=="undefined") +var abc2svg={} +abc2svg.grid2={do_grid:function(){var s,v,p_v,ix,cs,c_a_cs,bt,gch,voice_tb=this.get_voice_tb() +if(this.cfmt().grid2) +for(v=0;v'+ +cell[n]+'\n'} +function build_cell(cell,x,y,yl,hr){var line +if(cell.length>1){line=path+ +(x-wc/2).toFixed(1)+' '+ +yl.toFixed(1)+'l'+ +wc.toFixed(1)+' -'+hr.toFixed(1)+'"/>\n' +if(cell[1]){line+=path+ +(x-wc/2).toFixed(1)+' '+ +(yl-hr).toFixed(1)+'l'+ +(wc/2).toFixed(1)+' '+(hr/2).toFixed(1)+'"/>\n'+ +build_ch(cls+sf,x-wc/3,y,0)+ +build_ch(cls+sf,x,y-hr*.32,1)}else{line+=build_ch(cls+sf,x-wc*.2,y-hr/4,0)} +if(cell.length>=3){if(cell[3]){line+=path+ +x.toFixed(1)+' '+ +(yl-hr/2).toFixed(1)+'l'+ +(wc/2).toFixed(1)+' '+(hr/2).toFixed(1)+'"/>\n'+ +build_ch(cls+sf,x,y+hr*.3,2)+ +build_ch(cls+sf,x+wc/3,y,3)}else{line+=build_ch(cls+sf,x+wc*.2,y+hr/4,2)}}}else{line=build_ch(cls,x,y,0)} +return line} +hr=font.size*2.1 +if(wc:\n' +if(bar.slice(-1)==':') +line+=':\n' +x+=wc}} +abc.out_svg(line) +abc.vskip(hr*nr+6)} +if(!cfmt.gridfont) +abc.param_set_font("gridfont","serif 16") +font=abc.get_font('grid') +font_cl=abc.font_class(font) +cls=font_cl+" mid" +abc.add_style("\n.mid {text-anchor:middle}") +abc.set_font('grid') +txt=txt.split('\n') +while(1){ln=txt.shift() +if(!ln) +break +ln=ln.match(/[|:]+|[^|:\s]+/g) +bars[nr]=[] +cells[nr]=[] +i=-1 +while(1){cl=ln.shift() +if(!cl) +break +if(cl.match(/[:|]+/)){bars[nr][++i]=cl +cells[nr][i]=[]}else{if(!cells[nr][i]){bars[nr][++i]='|' +cells[nr][i]=[]} +if(cl=='.'||cl=='-') +cl='' +cells[nr][i].push(cl)}} +if(cells[nr][i].length) +bars[nr][++i]='|' +else +cells[nr][i]=null +if(i>nc) +nc=i +i=0 +while(1){cl=cells[nr][i++] +if(!cl) +break +if(cl.length==2){cl[2]=cl[1] +cl[1]=''} +w=abc.strwh(cl.join(''))[0] +if(w>wc) +wc=w} +nr++} +wc+=abc.strwh(' ')[0] +build_grid() +abc.blk_flush()},do_begin_end:function(of,type,opt,txt){var vt=this.get_voice_tb() +if(type!="grid"){of(type,opt,txt) +return} +txt=txt.replace(/#|=|b/g,function(x){switch(x){case'#':return"\u266f" +case'=':return"\u266e"} +return"\u266d"}) +if(opt.indexOf("chord-define")>=0) +this.cfmt().csdef=txt +if(opt.indexOf("noprint")<0){type+="3" +if(this.parse.state>=2){s=this.new_block(type) +s.text=txt}else{abc2svg.grid3.block_gen.call(this,null,{subtype:type,text:txt})}}},output_music:function(of){var ln,i,dt,ss,ntim,p_vc,s3,C=abc2svg.C,abc=this,s=abc.get_tsfirst(),vt=abc.get_voice_tb(),t=abc.cfmt().csdef,cs=[] +function add_cs(ss,ch){var s={type:C.REST,fname:ss.fname,istart:ss.istart,iend:ss.iend,v:p_vc.v,p_v:p_vc,time:ntim,st:0,fmt:ss.fmt,dur:0,dur_orig:0,invis:true,seqst:true,nhd:0,notes:[{pit:18,dur:0}]} +if(ch!='.'&&ch!='-'){abc.set_a_gch(s,[{type:'g',text:ch,otext:ch,istart:ss.istart,iend:ss.iend,font:abc.get_font("gchord"),pos:p_vc.pos.gch||C.SL_ABOVE}])} +if(!p_vc.last_sym){p_vc.sym=s}else{s.prev=p_vc.last_sym +s.prev.next=s} +p_vc.last_sym=s +s.ts_next=ss +s.ts_prev=ss.ts_prev +s.ts_prev.ts_next=s +ss.ts_prev=s +if(s.time==ss.time) +delete ss.seqst +return s} +if(t){p_vc={id:"grid3",v:vt.length,time:0,pos:{gst:0},scale:1,st:0,second:true,sls:[]} +vt.push(p_vc) +t=t.split('\n') +while(1){ln=t.shift() +if(!ln) +break +ln=ln.trimLeft() +if(ln[0]=='|') +ln=ln.slice(ln[1]==':'?2:1) +if(ln[ln.length-1]!='|') +ln=ln+'|' +ln=ln.match(/[|:]+|[^|:\s]+/g) +while(1){cl=ln.shift() +if(!cl) +break +if(cl[0]=='|'||cl[0]==':'){while(s&&!s.dur) +s=s.ts_next +if(!s) +break +ss=s +while(s&&!s.bar_type) +s=s.ts_next +if(!cs.length) +cs=['.'] +ntim=ss.time +dt=(s.time-ntim)/cs.length +s3=null +for(i=0;i=0) +continue +switch(t){case"/":gch.text="\ue101";continue +case"%":gch.text="\ue500";continue +case"%%":gch.text="\ue501";continue} +if(fmt.jzreg){t=t.replace(fmt.jzRE,function(x){return fmt.jzrep[x]})} +if(fmt.jazzchord==1){if(t[0]=='(') +t=t.slice(1,-1) +t=t.split('(') +r=jzch(t[0]) +if(t.length>1) +r+='('+jzch(t[1])}else{r=t} +if(gch.text[0]=='(') +gch.text='('+r+')' +else +gch.text=r} +of(s)},set_fmt:function(of,cmd,parm){var i,k,s,cfmt=this.cfmt() +if(cmd=="jazzchord"){cfmt.jazzchord=this.get_bool(parm) +if(!cfmt.jazzchord) +return +if(parm[0]=='2') +cfmt.jazzchord=2 +if(!cfmt.jzreg){cfmt.jzreg="-|°|º|ᵒ|0|\\^" +cfmt.jzrep=Object.create(abc2svg.jazzchord.defrep) +cfmt.jzRE=new RegExp(cfmt.jzreg,'g')} +if(parm&&parm.indexOf('=')>0){parm=parm.split(/[\s]+/) +for(cmd=0;cmd=0){if(s){cfmt.jzrep[k]=s}else{cfmt.jzreg=cfmt.jzreg.replace(k,'') +cfmt.jzreg=cfmt.jzreg.replace('||','|') +delete cfmt.jzrep[k]}}else{cfmt.jzreg+='|'+k +cfmt.jzrep[k]=s} +cfmt.jzRE=new RegExp(cfmt.jzreg,'g')}} +return} +of(cmd,parm)},set_hooks:function(abc){abc.gch_build=abc2svg.jazzchord.gch_build.bind(abc,abc.gch_build) +abc.set_format=abc2svg.jazzchord.set_fmt.bind(abc,abc.set_format) +abc.add_style("\ +\n.jc7{font-size:90%}\ +\n.jc8{baseline-shift:25%;font-size:75%;letter-spacing:-0.05em}\ +\n.jc9{font-size:75%;letter-spacing:-0.05em}\ +") +abc.param_set_font("setfont-7","* * class=jc7") +abc.param_set_font("setfont-8","* * class=jc8") +abc.param_set_font("setfont-9","* * class=jc9")}} +if(!abc2svg.mhooks) +abc2svg.mhooks={} +abc2svg.mhooks.jazzchord=abc2svg.jazzchord.set_hooks diff --git a/lib/ChordPro/res/abc/abc2svg/jianpu-1.js b/lib/ChordPro/res/abc/abc2svg/jianpu-1.js new file mode 100644 index 00000000..db7085e7 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/jianpu-1.js @@ -0,0 +1,248 @@ +//jianpu.js-module to output jiănpŭ(简谱)music sheets +if(typeof abc2svg=="undefined") +var abc2svg={} +abc2svg.jianpu={k_tb:["Cb","Gb","Db","Ab","Eb","Bb","F","C","G","D","A","E","B","F#","C#"],cde2fcg:new Int8Array([0,2,4,-1,1,3,5]),cgd2cde:new Int8Array([0,-4,-1,-5,-2,-6,-3,0,-4,-1,-5,-2,-6,-3,0]),acc2:new Int8Array([-2,-1,3,1,2]),acc_tb:["\ue264","\ue260",,"\ue262","\ue263","\ue261"],calc_beam:function(of,bm,s1){if(!s1.p_v.jianpu) +return of(bm,s1)},output_music:function(of){var p_v,v,C=abc2svg.C,abc=this,cur_sy=abc.get_cur_sy(),voice_tb=abc.get_voice_tb() +function ov_def(v){var s1,tim,s=p_v.sym +while(s){s1=s.ts_prev +if(!s.invis&&s.dur&&s1.v!=v&&s1.st==s.st&&s1.time==s.time){while(1){if(!s1.prev||s1.prev.bar_type) +break +s1=s1.prev} +while(!s1.bar_type){s1.dy=14 +s1.notes[0].pit=30 +if(s1.type==C.REST) +s1.combine=-1 +s1=s1.next} +while(1){s.dy=-14 +s.notes[0].pit=20 +if(!s.next||s.next.bar_type||s.next.time>=s1.time) +break +s=s.next}} +s=s.next}} +function set_head(){var v,p_v,mt,s2,sk,s,tsfirst=abc.get_tsfirst() +for(v=0;v=voice_tb.length) +return +mt=p_v.meter.a_meter[0] +sk=p_v.key +s2=p_v.sym +s={type:C.BLOCK,subtype:"text",time:s2.time,dur:0,v:0,p_v:p_v,st:0,fmt:s2.fmt,seqst:true,text:(sk.k_mode+1)+"="+ +(abc2svg.jianpu.k_tb[sk.k_sf+7+ +abc2svg.jianpu.cde2fcg[sk.k_mode]]),font:abc.get_font("text")} +if(mt) +s.text+=' '+(mt.bot?(mt.top+'/'+mt.bot):mt.top) +s2=tsfirst +s.next=s2.next +if(s.next) +s.next.prev=s +s.prev=s2 +s2.next=s +s.ts_next=s2.ts_next +s.ts_next.ts_prev=s +s.ts_prev=s2 +s2.ts_next=s} +function slice(s){var n,s2,s3,jn=s.type==C.REST?0:8 +if(s.dur>=C.BLEN) +n=3 +else if(s.dur==C.BLEN/2) +n=1 +else +n=2 +s.notes[0].dur=s.dur=s.dur_orig=C.BLEN/4 +delete s.fmr +while(--n>=0){s2={type:C.REST,v:s.v,p_v:s.p_v,st:s.st,dur:C.BLEN/4,dur_orig:C.BLEN/4,fmt:s.fmt,stem:0,multi:0,nhd:0,notes:[{dur:s.dur,pit:s.notes[0].pit,jn:jn}],xmx:0,noplay:true,time:s.time+C.BLEN/4,prev:s,next:s.next} +s.next=s2 +if(s2.next) +s2.next.prev=s2 +if(!s.ts_next){s.ts_next=s2 +if(s.soln) +s.soln=false +s2.ts_prev=s +s2.seqst=true}else{for(s3=s.ts_next;s3;s3=s3.ts_next){if(s3.times2.time){s2.seqst=true +s3=s3.ts_prev} +s2.ts_next=s3.ts_next +s2.ts_prev=s3 +if(s2.ts_next) +s2.ts_next.ts_prev=s2 +s3.ts_next=s2 +break}} +s=s2}} +function set_note(s,sf){var i,m,note,p,pit,a,nn,delta=abc2svg.jianpu.cgd2cde[sf+7]-2 +s.stem=-1 +s.stemless=true +if(s.sls){for(i=0;i=C.BLEN/2&&!s.invis) +slice(s) +if(s.a_dd){for(i=0;i=C.BLEN/2&&!s.invis) +slice(s) +continue +case C.NOTE:set_note(s,sf) +break +case C.GRACE:for(g=s.extra;g;g=g.next) +set_note(g,sf) +break}}} +set_head() +for(v=0;v\n') +y-=2.5 +while(++n<=nl){s=s1 +while(1){if(s.nflags&&s.nflags>=n){s3=s +while(s!=s2){if(s.next.beam_br1||(s.next.beam_br2&&n>2)||(s.next.nflags&&s.next.nflags'+p+'\n')} +function out_txt(x,y,p){out_svg(''+p+'\n')} +function draw_hd(s,x,y){var m,note,ym +for(m=0;m<=s.nhd;m++){note=s.notes[m] +out_txt(x-3.5,y+8,"01234567-"[note.jn]) +if(note.acc) +out_mus(x-12,y+12,abc2svg.jianpu.acc_tb[note.acc+2]) +if(note.jo>2){out_mus(x-1,y+22,dot) +if(note.jo>3){y+=3 +out_mus(x-1,y+22,dot)}}else if(note.jo<2){ym=y+4 +if(m==0&&s.nflags>0) +ym-=2.5*s.nflags +out_mus(x-1,ym,dot) +if(note.jo<1){ym-=3 +out_mus(x-1,ym,dot)}} +y+=20}} +function draw_note(s){var sc=1,x=s.x,y=staff_tb[s.st].y +if(s.dy) +y+=s.dy +if(s.grace){out_svg('\n') +abc.stv_g().g++ +x=0 +y=0 +sc=.5} +draw_hd(s,x,y) +if(s.nflags>=0&&s.dots) +out_mus(x+8*sc,y+13*sc,dot) +if(s.grace){out_svg('\n') +abc.stv_g().g--}} +for(s=p_voice.sym;s;s=s.next){if(s.invis) +continue +switch(s.type){case C.METER:abc.draw_meter(s) +break +case C.NOTE:case C.REST:draw_note(s) +break +case C.GRACE:for(g=s.extra;g;g=g.next) +draw_note(g) +break}} +for(s=p_voice.sym;s;s=s.next){if(s.invis) +continue +switch(s.type){case C.NOTE:case C.REST:nl=s.nflags +if(nl<=0) +continue +y=staff_tb[s.st].y +s2=s +while(s.next&&s.next.nflags>0){s=s.next +if(s.nflags>nl) +nl=s.nflags +if(s.beam_end) +break} +if(s.dy) +y+=s.dy +draw_dur(s2,s2.x,y,s,1,nl) +break}}},set_fmt:function(of,cmd,param){if(cmd=="jianpu"){this.set_v_param("jianpu",param) +return} +of(cmd,param)},set_pitch:function(of,last_s){of(last_s) +if(!last_s) +return +var C=abc2svg.C +for(var s=this.get_tsfirst();s;s=s.ts_next){if(!s.p_v.jianpu) +continue +switch(s.type){case C.KEY:if(s.prev.type==C.CLEF||s.v!=0) +s.a_gch=null +break +case C.NOTE:s.ymx=20*s.nhd+22 +if(s.notes[s.nhd].jo>2){s.ymx+=3 +if(s.notes[s.nhd].jo>3) +s.ymx+=2} +s.ymn=0 +break}}},set_vp:function(of,a){var i,p_v=this.get_curvoice() +for(i=0;i=0){json+="!!! to_json: loop in '"+attr+"' !!!" +break} +objstk.push(val) +if(Array.isArray(val)){if(val.length==0){json+="[]" +break} +h='[\n';l=val.length +for(i=0;i1',n2:'2',n3:'3',n4:'4',n5:'5',n6:'6',n7:'7',nq:'\'',nc:',',nh:'',nw:'',aff:'',af:'',nn:'',ns:'',nss:''},decos:{n1:"9 n1 0 0 0",n2:"9 n2 0 0 0",n3:"9 n3 0 0 0",n4:"9 n4 0 0 0",n5:"9 n5 0 0 0",n6:"9 n6 0 0 0",n7:"9 n7 0 0 0",q:"0 nq 0 0 0",c:"0 nc 0 0 0",h:"0 nh 0 0 0",w:"0 nw 0 0 0",aff:"0 aff 0 0 0",af:"0 af 0 0 0",n:"0 nn 0 0 0",s:"0 ns 0 0 0",ss:"0 nss 0 0 0"},output_music:function(of){var C=abc2svg.C,abc=this,cfmt=abc.cfmt(),cur_sy=abc.get_cur_sy(),voice_tb=abc.get_voice_tb(),sf=voice_tb[0].key.k_sf,delta=abc2svg.mdnn.cgd2cde[sf+7]-2 +var s,s2,note,pit,nn,p,a,i,prev_oct=-10 +if(!cfmt.mdnn){of() +return} +delete voice_tb[0].key.k_a_acc +voice_tb[0].clef.invis=true +cur_sy.staves[0].stafflines="..." +for(s=voice_tb[0].sym;s;s=s.next){switch(s.type){case C.CLEF:s.invis=true +continue +case C.KEY:sf=s.k_sf +delta=abc2svg.mdnn.cgd2cde[sf+7]-2 +nn=sf-s.k_old_sf +s.k_old_sf=0 +s.k_sf=nn +continue +default:continue +case C.NOTE:break} +note=s.notes[0] +p=note.pit +pit=p+delta +nn=((pit+77)%7)+1 +abc.dh_put('n'+nn,s,note) +note.pit=23 +s.stem=1 +nn=(pit/7)|0 +if(nn>prev_oct){if(prev_oct!=-10) +abc.dh_put('q',s,note) +prev_oct=nn}else if(nn=C.BLEN/2) +abc.dh_put(s.dur>=C.BLEN?'w':'h',s,note) +a=note.acc +if(a){note.acc=0 +nn=abc2svg.mdnn.cde2fcg[(p+5+16*7)%7]-sf +if(a!=3) +nn+=a*7 +nn=((((nn+1+21)/7)|0)+2-3+32*5)%5 +abc.dh_put(abc2svg.mdnn.acc_tb[nn],s,note)} +if(s.sls){for(i=0;i=0){i=l=0 +while(1){j=parse.file.indexOf('\n',i) +if(j<0||j>idx) +break +l++;i=j+1} +c=idx-i} +h="" +if(fn){h=fn +if(l) +h+=":"+(l+1)+":"+(c+1);h+=" "} +switch(sev){case 0:h+="Warning: ";break +case 1:h+="Error: ";break +default:h+="Internal bug: ";break} +user.errmsg(h+txt,l,c)} +function error(sev,s,msg,a1,a2,a3,a4){var i,j,regex,tmp +if(!sev&&cfmt.quiet) +return +if(s){if(s.err) +return +s.err=true} +if(user.textrans){tmp=user.textrans[msg] +if(tmp) +msg=tmp} +if(arguments.length>3) +msg=msg.replace(/\$./g,function(a){switch(a){case'$1':return a1 +case'$2':return a2 +case'$3':return a3 +default:return a4}}) +if(s&&s.fname) +errbld(sev,msg,s.fname,s.istart) +else +errbld(sev,msg)} +function scanBuf(){this.index=0;scanBuf.prototype.char=function(){return this.buffer[this.index]} +scanBuf.prototype.next_char=function(){return this.buffer[++this.index]} +scanBuf.prototype.get_int=function(){var val=0,c=this.buffer[this.index] +while(c>='0'&&c<='9'){val=val*10+Number(c);c=this.next_char()} +return val}} +function syntax(sev,msg,a1,a2,a3,a4){var s={fname:parse.fname,istart:parse.istart+parse.line.index} +error(sev,s,msg,a1,a2,a3,a4)} +function js_inject(js){eval('"use strict";\n'+js)} +var dd_tb={},a_de,cross +var decos={dot:"0 stc 6 1.5 1",tenuto:"0 emb 6 4 3",slide:"1 sld 3 7 1",arpeggio:"2 arp 12 10 3",roll:"3 roll 5,4 5 6",lowermordent:"3 lmrd 6,5 4 6",uppermordent:"3 umrd 6,5 4 6",trill:"3 trl 14 5 8",upbow:"3 upb 10,2 3 7",downbow:"3 dnb 9 4 6",gmark:"3 grm 7 4 6",wedge:"0 wedge 8 1.5 1",longphrase:"5 lphr 0 1 16",mediumphrase:"5 mphr 0 1 16",shortphrase:"5 sphr 0 1 16",turnx:"3 turnx 7,2.5 5 6",invertedturn:"3 turn 7,2 5 6","0":"3 fng 5,5 3 3 0","1":"3 fng 5,5 3 3 1","2":"3 fng 5,5 3 3 2","3":"3 fng 5,5 3 3 3","4":"3 fng 5,5 3 3 4","5":"3 fng 5,5 3 3 5",plus:"3 dplus 8,2 2 4","+":"3 dplus 8,2 2 4",">":"5 accent 3.5,3.5 4 4",accent:"5 accent 3.5,3.5 4 4",emphasis:"5 accent 3.5,3.5 4 4",marcato:"3 marcato 9 5 5","^":"3 marcato 9 5 5",mordent:"3 lmrd 6,5 4 6",open:"3 opend 8 3 3",snap:"3 snap 10 3 3",thumb:"3 thumb 10 3 3",turn:"3 turn 7,2.5 5 6","trill(":"5 ltr 8 0 0","trill)":"5 ltr 8 0 0","8va(":"5 8va 12 6 6","8va)":"5 8va 12 6 6","8vb(":"4 8vb 10,5 6 6","8vb)":"4 8vb 10,5 6 6","15ma(":"5 15ma 12 9 9","15ma)":"5 15ma 12 9 9","15mb(":"4 15mb 12 9 9","15mb)":"4 15mb 12 9 9",breath:"5 brth 0 1 16",caesura:"5 caes 0 1 20",short:"5 short 0 1 16",tick:"5 tick 0 1 16",coda:"5 coda 22,5 10 10",dacapo:"5 dacs 16 20 20 Da Capo",dacoda:"5 dacs 16 20 20 Da Coda","D.C.":"5 dcap 16,3 6 6","D.S.":"5 dsgn 16,3 6 6","D.C.alcoda":"5 dacs 16 32 32 D.C. al Coda","D.S.alcoda":"5 dacs 16 32 32 D.S. al Coda","D.C.alfine":"5 dacs 16 32 32 D.C. al Fine","D.S.alfine":"5 dacs 16 32 32 D.S. al Fine",fermata:"5 hld 12 7.5 7.5",fine:"5 dacs 16 12 12 Fine",invertedfermata:"7 hld 12 8 8",segno:"5 sgno 22,2 5 5",f:"6 f 12,5 3 4",ff:"6 ff 12,5 8 5",fff:"6 fff 12,5 11 9",ffff:"6 ffff 12,5 15 12",mf:"6 mf 12,5 8 10",mp:"6 mp 12,5 9 10",p:"6 p 12,5 3 6",pp:"6 pp 12,5 8 9",ppp:"6 ppp 12,5 14 11",pppp:"6 pppp 12,5 14 17",pralltriller:"3 umrd 6,5 4 6",sfz:"6 sfz 12,5 9 9",ped:"7 ped 14 6 10","ped-up":"7 pedoff 12 4 4","ped(":"7 lped 14 1 1","ped)":"7 lped 14 1 1","crescendo(":"6 cresc 15,2 0 0","crescendo)":"6 cresc 15,2 0 0","<(":"6 cresc 15,2 0 0","<)":"6 cresc 15,2 0 0","diminuendo(":"6 dim 15,2 0 0","diminuendo)":"6 dim 15,2 0 0",">(":"6 dim 15,2 0 0",">)":"6 dim 15,2 0 0","-(":"8 gliss 0 0 0","-)":"8 gliss 0 0 0","~(":"8 glisq 0 0 0","~)":"8 glisq 0 0 0",invisible:"32 0 0 0 0",beamon:"33 0 0 0 0",trem1:"34 0 0 0 0",trem2:"34 0 0 0 0",trem3:"34 0 0 0 0",trem4:"34 0 0 0 0",xstem:"35 0 0 0 0",beambr1:"36 0 0 0 0",beambr2:"36 0 0 0 0",rbstop:"37 0 0 0 0","/":"38 0 0 6 6","//":"38 0 0 6 6","///":"38 0 0 6 6","beam-accel":"39 0 0 0 0","beam-rall":"39 0 0 0 0",stemless:"40 0 0 0 0",rbend:"41 0 0 0 0",editorial:"42 0 0 0 0","sacc-1":"3 sacc-1 6,4 4 4",sacc3:"3 sacc3 6,5 4 4",sacc1:"3 sacc1 6,4 4 4",courtesy:"43 0 0 0 0","cacc-1":"3 cacc-1 0 0 0",cacc3:"3 cacc3 0 0 0",cacc1:"3 cacc1 0 0 0","tie(":"44 0 0 0 0","tie)":"44 0 0 0 0"},f_near=[d_near,d_slide,d_arp],f_note=[null,null,null,d_upstaff,d_upstaff],f_staff=[null,null,null,null,null,d_upstaff,d_upstaff,d_upstaff] +function y_get(st,up,x,w){var y,p_staff=staff_tb[st],i=(x/2)|0,j=((x+w)/2)|0 +if(i<0) +i=0 +if(j>=YSTEP){j=YSTEP-1 +if(i>j) +i=j} +if(up){y=p_staff.top[i++] +while(i<=j){if(yp_staff.bot[i]) +y=p_staff.bot[i];i++}} +return y} +function y_set(st,up,x,w,y){var p_staff=staff_tb[st],i=(x/2)|0,j=((x+w)/2)|0 +if(i<0) +i=0 +if(j>=YSTEP){j=YSTEP-1 +if(i>j) +i=j} +if(up){while(i<=j){if(p_staff.top[i]y) +p_staff.bot[i]=y;i++}}} +function up3(s,pos){switch(pos&0x07){case C.SL_ABOVE:return 1 +case C.SL_BELOW:return 0} +return!s.second} +function up6(s,pos){switch(pos&0x07){case C.SL_ABOVE:return true +case C.SL_BELOW:return false} +if(s.multi) +return s.multi>0 +if(!s.p_v.have_ly) +return false +return(s.pos.voc&0x07)!=C.SL_ABOVE} +function d_arp(de){var m,h,dx,s=de.s,dd=de.dd,xc=dd.wr +if(s.type==C.NOTE){for(m=0;m<=s.nhd;m++){if(s.notes[m].acc){dx=s.notes[m].shac}else{dx=1-s.notes[m].shhd +switch(s.head){case C.SQUARE:dx+=3.5 +break +case C.OVALBARS:case C.OVAL:dx+=2 +break}} +if(dx>xc) +xc=dx}} +h=3*(s.notes[s.nhd].pit-s.notes[0].pit)+4;m=dd.h +if(h0&&y<24){y=(((y+9)/6)|0)*6-6} +if(up){y+=dd.hd +s.ymx=y+dd.h}else if(dd.name[0]=='w'){de.inv=true +y-=dd.h +s.ymn=y}else{y-=dd.h +s.ymn=y-dd.hd} +de.x-=dd.wl +de.y=y +if(s.type==C.NOTE) +de.x+=s.notes[s.stem>=0?0:s.nhd].shhd +if(dd.name[0]=='d'){if(!(s.beam_st&&s.beam_end)){if(up){if(s.stem>0) +de.x+=3.5}else{if(s.stem<0) +de.x-=3.5}}else{if(up&&s.stem>0){y=s.y+(y-s.y)*.6 +if(y>=27){de.y=y +s.ymx=de.y+dd.h}}}}} +function d_slide(de){var m,dx,s=de.s,yc=s.notes[0].pit,xc=5 +for(m=0;m<=s.nhd;m++){if(s.notes[m].acc){dx=4+s.notes[m].shac}else{dx=5-s.notes[m].shhd +switch(s.head){case C.SQUARE:dx+=3.5 +break +case C.OVALBARS:case C.OVAL:dx+=2 +break}} +if(s.notes[m].pit<=yc+3&&dx>xc) +xc=dx} +de.x-=xc;de.y=3*(yc-18)} +function d_trill(de){if(de.ldst) +return +var y,w,tmp,dd=de.dd,de2=de.prev,up=de.start.up,s2=de.s,st=s2.st,s=de.start.s,x=s.x +function sh_st(){var de3,de2=de.start,s=de2.s,i=de2.ix +while(--i>=0){de3=a_de[i] +if(!de3||de3.s!=s) +break} +while(1){i++ +de3=a_de[i] +if(!de3||de3.s!=s) +break +if(de3==de2) +continue +if(!(up^de3.up)&&(de3.dd.name=="trill"||de3.dd.func==6)){x+=de3.dd.wr+2 +break}}} +function sh_en(){var de3,i=de.ix +while(--i>0){de3=a_de[i] +if(!de3||de3.s!=s2) +break} +while(1){i++ +de3=a_de[i] +if(!de3||de3.s!=s2) +break +if(de3==de) +continue +if(!(up^de3.up)&&de3.dd.func==6){w-=de3.dd.wl +break}}} +if(de2){x=de2.s.x+de.dd.wl+2 +de2.val-=de2.dd.wr +if(de2.val<8) +de2.val=8} +de.st=st +de.up=up +sh_st() +if(de.defl.noen){w=de.x-x +if(w<20){x=de.x-20-3;w=20}}else{w=s2.x-x-4 +sh_en(de) +if(w<20) +w=20} +y=y_get(st,up,x-dd.wl,w) +if(up){tmp=staff_tb[s.st].topbar+2 +if(ytmp) +y=tmp +y-=dd.h} +if(de2){if(up){if(y=de2.y){y=de2.y}else{do{de2.y=y +de2=de2.prev}while(de2)}}} +de.lden=false;de.has_val=true;de.val=w;de.x=x;de.y=y +if(up) +y+=dd.h;else +y-=dd.hd +y_set(st,up,x,w,y) +if(up) +s.ymx=s2.ymx=y +else +s.ymn=s2.ymn=y} +function d_upstaff(de){if(de.ldst) +return +if(de.start){d_trill(de) +return} +var y,inv,up=de.up,s=de.s,dd=de.dd,x=de.x,w=dd.wl+dd.wr +switch(dd.glyph){case"brth":case"caes":case"lphr":case"mphr":case"sphr":case"short":case"tick":y=staff_tb[s.st].topbar+2+dd.hd +if(s.type==C.BAR){s.invis=1}else{if(dd.glyph=="brth"&&y=0?0:s.nhd].shhd;switch(dd.ty){case'@':case'<':case'>':y=de.y +break} +if(y==undefined){if(up){y=y_get(s.st,true,x-dd.wl,w) ++dd.hd +if(de.y>y) +y=de.y +s.ymx=y+dd.h}else{y=y_get(s.st,false,x-dd.wl,w) +-dd.h +if(de.y5&&x>realwidth-dd.wr) +de.x=x=realwidth-dd.wr +if(up) +y_set(s.st,1,x-dd.wl,w,y+dd.h) +else +y_set(s.st,0,x-dd.wl,w,y-dd.hd) +de.y=y} +function deco_add(param){var dv=param.match(/(\S*)\s+(.*)/);decos[dv[1]]=dv[2]} +function deco_def(nm,nmd){if(!nmd) +nmd=nm +var a,dd,dd2,nm2,c,i,elts,str,hd,text=decos[nmd] +if(!text&&/\d[()]$/.test(nmd)) +text=decos[nmd.replace(/\d/,'')] +if(!text){if(cfmt.decoerr) +error(1,null,"Unknown decoration '$1'",nm) +return} +a=text.match(/(\d+)\s+(.+?)\s+([0-9.,]+)\s+([0-9.]+)\s+([0-9.]+)/) +if(!a){error(1,null,"Invalid decoration '$1'",nm) +return} +var c_func=Number(a[1]),h=a[3],wl=parseFloat(a[4]),wr=parseFloat(a[5]) +if(isNaN(c_func)){error(1,null,"%%deco: bad C function value '$1'",a[1]) +return} +if(c_func>10&&(c_func<32||c_func>44)){error(1,null,"%%deco: bad C function index '$1'",c_func) +return} +if(h.indexOf(',')>0){h=h.split(',') +hd=h[1] +h=h[0]}else{hd=0} +if(h>50||wl>80||wr>80){error(1,null,"%%deco: abnormal h/wl/wr value '$1'",text) +return} +dd=dd_tb[nm] +if(!dd){dd={name:nm} +dd_tb[nm]=dd} +dd.func=nm.indexOf("head-")==0?9:c_func;dd.glyph=a[2];dd.h=Number(h) +dd.hd=Number(hd) +dd.wl=wl;dd.wr=wr;str=text.replace(a[0],'').trim() +if(str){if(str[0]=='"') +str=str.slice(1,-1);if(str[0]=='@'){c=str.match(/^@([0-9.-]+),([0-9.-]+);?/) +if(!c){error(1,null,"%%deco: bad position '$1'",str) +return} +dd.dx=+c[1] +dd.dy=+c[2] +str=str.replace(c[0],'')} +dd.str=str} +if(dd.func==6&&dd.str==undefined) +dd.str=nm +c=nm.slice(-1) +if(c=='('||(c==')'&&nm.indexOf('(')<0)){dd.str=null;nm2=nm.slice(0,-1)+(c=='('?')':'(');dd2=dd_tb[nm2] +if(dd2){if(c=='('){dd.dd_en=dd2;dd2.dd_st=dd}else{dd.dd_st=dd2;dd2.dd_en=dd}}} +return dd} +function do_ctie(nm,s,nt1){var nt2=cross[nm],nm2=nm.slice(0,-1)+(nm.slice(-1)=='('?')':'(') +if(nt2){error(1,s,"Conflict on !$1!",nm) +return} +nt1.s=s +nt2=cross[nm2] +if(!nt2){cross[nm]=nt1 +return} +if(nm.slice(-1)==')'){nt2=nt1 +nt1=cross[nm2]} +cross[nm2]=null +if(nt1.midi!=nt2.midi||nt1.s.time+nt1.s.dur!=nt2.s.time){error(1,s,"Bad tie")}else{nt1.tie_ty=C.SL_AUTO +nt1.tie_e=nt2 +nt2.tie_s=nt1 +nt1.s.ti1=nt2.s.ti2=true}} +function get_dd(nm){var ty,p,dd=dd_tb[nm] +if(dd) +return dd +if("<>^_@".indexOf(nm[0])>=0&&!/^([>^]|[<>]\d?[()])$/.test(nm)){ty=nm[0] +if(ty=='@'){p=nm.match(/@([-\d]+),([-\d]+)/) +if(p) +ty=p[0] +else +ty=''} +dd=deco_def(nm,nm.replace(ty,''))}else{dd=deco_def(nm)} +if(!dd) +return +if(ty){if(ty[0]=='@'){dd.x=Number(p[1]) +dd.y=Number(p[2]) +ty='@'} +dd.ty=ty} +return dd} +function deco_cnv(s,prev){var i,j,dd,nm,note,s1,court +while(1){nm=a_dcn.shift() +if(!nm) +break +dd=get_dd(nm) +if(!dd) +continue +switch(dd.func){case 0:if(s.type==C.BAR&&nm=="dot"){s.bar_dotted=true +continue} +case 1:case 2:if(!s.notes){error(1,s,errs.must_note_rest,nm) +continue} +break +case 4:case 5:i=nm.match(/1?[85]([vm])([ab])([()])/) +if(i){j=i[1]=='v'?1:2 +if(i[2]=='b') +j=-j +if(!s.ottava) +s.ottava=[] +s.ottava[i[3]=='('?0:1]=j +glovar.ottava=1} +break +case 8:if(s.type!=C.NOTE){error(1,s,errs.must_note,nm) +continue} +note=s.notes[s.nhd] +if(!note.a_dd) +note.a_dd=[] +note.a_dd.push(dd) +continue +case 9:if(!s.notes){error(1,s,errs.must_note_rest,nm) +continue} +for(j=0;j<=s.nhd;j++){note=s.notes[j] +note.invis=true +if(!note.a_dd) +note.a_dd=[] +note.a_dd.push(dd)} +continue +case 10:if(s.notes){for(j=0;j<=s.nhd;j++) +s.notes[j].color=nm}else{s.color=nm} +break +case 32:s.invis=true +break +case 33:if(s.type!=C.BAR){error(1,s,"!beamon! must be on a bar") +continue} +s.beam_on=true +break +case 34:if(s.type!=C.NOTE||!prev||prev.type!=C.NOTE||s.dur!=prev.dur){error(1,s,"!$1! must be on the last of a couple of notes",nm) +continue} +s.trem2=true;s.beam_end=true;s.beam_st=false;prev.beam_st=true;prev.beam_end=false;s.ntrem=prev.ntrem=Number(nm[4]);for(j=0;j<=s.nhd;j++) +s.notes[j].dur*=2;for(j=0;j<=prev.nhd;j++) +prev.notes[j].dur*=2 +break +case 35:if(s.type!=C.NOTE){error(1,s,errs.must_note,nm) +continue} +s.xstem=true;break +case 36:if(s.type!=C.NOTE){error(1,s,errs.must_note,nm) +continue} +if(nm[6]=='1') +s.beam_br1=true +else +s.beam_br2=true +break +case 37:s.rbstop=1 +break +case 38:if(s.type!=C.NOTE){error(1,s,errs.must_note,nm) +continue} +s.trem1=true;s.ntrem=nm.length +break +case 39:if(s.type!=C.NOTE){error(1,s,errs.must_note,nm) +continue} +s.feathered_beam=nm[5]=='a'?1:-1;break +case 40:s.stemless=true +break +case 41:s.rbstop=2 +break +case 42:if(s.type!=C.NOTE){error(1,s,errs.must_note,nm) +continue} +if(!s.notes[0].acc) +continue +nm="sacc"+s.notes[0].acc.toString() +dd=dd_tb[nm] +if(!dd){dd=deco_def(nm) +if(!dd){error(1,s,errs.bad_val,"!editorial!") +continue}} +delete s.notes[0].acc +curvoice.acc[s.notes[0].pit+19]=0 +break +case 43:if(s.type!=C.NOTE){error(1,s,errs.must_note,nm) +continue} +j=curvoice.acc[s.notes[0].pit+19] +if(s.notes[0].acc||!j) +continue +court=1 +break +case 44:if(s.type!=C.NOTE){error(1,s,errs.must_note,nm) +continue} +do_ctie(nm,s,s.notes[0]) +continue} +if(!s.a_dd) +s.a_dd=[] +s.a_dd.push(dd)} +if(court){a_dcn.push("cacc"+j) +dh_cnv(s,s.notes[0])}} +function dh_cnv(s,nt){var k,nm,dd +while(1){nm=a_dcn.shift() +if(!nm) +break +dd=get_dd(nm) +if(!dd) +continue +switch(dd.func){case 0:case 1:case 3:case 4:case 8:break +default:error(1,s,"Cannot have !$1! on a head",nm) +continue +case 9:nt.invis=true +break +case 32:nt.invis=true +continue +case 10:nt.color=nm +continue +case 40:s.stemless=true +continue +case 44:do_ctie(nm,s,nt) +continue} +if(!nt.a_dd) +nt.a_dd=[] +nt.a_dd.push(dd)}} +function deco_update(s,dx){var i,de,nd=a_de.length +for(i=0;i':w=wr+dd.wl+dd.wr+6 +if(s.wrwl) +wl=w}} +return wl} +Abc.prototype.draw_all_deco=function(){if(!a_de.length) +return +var de,dd,s,note,f,st,x,y,y2,ym,uf,i,str,a,new_de=[],ymid=[] +st=nstaff;y=staff_tb[st].y +while(--st>=0){y2=staff_tb[st].y;ymid[st]=(y+24+y2)*.5;y=y2} +while(1){de=a_de.shift() +if(!de) +break +dd=de.dd +if(!dd) +continue +if(dd.dd_en) +continue +s=de.s +f=dd.glyph;i=f.indexOf('/') +if(i>0){if(s.stem>=0) +f=f.slice(0,i) +else +f=f.slice(i+1)} +if(f_staff[dd.func]) +set_sscale(s.st) +else +set_scale(s);st=de.st;if(!staff_tb[st].topbar) +continue +x=de.x+(dd.dx||0) +y=de.y+staff_tb[st].y+(dd.dy||0) +if(de.m!=undefined){note=s.notes[de.m];if(note.shhd) +x+=note.shhd*stv_g.scale}else if(dd.func==6&&((de.pos&C.SL_ALI_MSK)==C.SL_CENTER||((de.pos&C.SL_ALI_MSK)==0&&!s.fmt.dynalign))&&((de.up&&st>0)||(!de.up&&stym)){y2=y_get(st,!de.up,de.x,de.val) ++staff_tb[st].y +if(de.up) +y2-=dd.h +if((de.up&&y2>ym)||(!de.up&&y2=10) +continue +pos=0 +break +case 3:case 4:case 5:pos=s.pos.orn +break +case 6:pos=s.pos.dyn +break} +switch(dd.ty){case'^':pos=(pos&~0x07)|C.SL_ABOVE +break +case'_':pos=(pos&~0x07)|C.SL_BELOW +break +case'<':case'>':pos=(pos&0x07)|C.SL_CLOSE +if(dd.ty=='<'){x-=dd.wr+8 +if(s.notes[0].acc) +x-=8}else{x+=dd.wl+8} +y=3*(s.notes[0].pit-18) +-(dd.h-dd.hd)/2 +break +case'@':x+=dd.x +y+=dd.y +break} +if((pos&0x07)==C.SL_HIDDEN) +continue +de={s:s,dd:dd,st:s.st,ix:a_de.length,defl:{},x:x,y:y} +if(pos) +de.pos=pos +up=0 +if(dd.ty=='^'){up=1}else if(dd.ty=='_'){}else{switch(dd.func){case 0:if(s.multi) +up=s.multi>0 +else +up=s.stem<0 +break +case 3:case 5:up=up3(s,pos) +break +case 6:case 7:up=up6(s,pos) +break}} +de.up=up +if(dd.name.indexOf("inverted")>=0) +de.inv=1 +if(s.type==C.BAR&&!dd.ty) +de.x-=s.wl/2-2 +a_de.push(de) +if(dd.dd_en){de.ldst=true}else if(dd.dd_st){de.lden=true;de.defl.nost=true} +if(f_near[dd.func]) +f_near[dd.func](de)}} +function create_dh(s,m){var de,k,dd,note=s.notes[m],nd=note.a_dd.length,x=s.x +for(k=0;k'){de.x+=dd.wl+8}}} +a_de.push(de) +if(dd.dd_en){de.ldst=true}else if(dd.dd_st){de.lden=true;de.defl.nost=true}}} +function create_all(s){if(s.invis&&s.play) +return +if(s.a_dd) +create_deco(s) +if(s.notes){for(var m=0;m=0){de3=a_de[j] +if(!de3.start) +continue +if(de3.s.time0&s.bar_num&&s.bar_num%cfmt.measurenb) +x+=6 +if(s.type!=C.BAR){w=s.rbstop?0:s.x-realwidth+4}else if((s.bar_type.length>1&&s.bar_type!="[]")||s.bar_type=="]"){if(s1.st>0&&!(cur_sy.staves[s1.st-1].flags&STOP_BAR)) +w=s.wl +else if(s.bar_type.slice(-1)==':') +w=12 +else if(s.bar_type[0]!=':') +w=0 +else +w=8}else{w=(s.rbstop&&!s.rbstart)?0:8} +w=(s.x-x-w) +if(!s.next&&!s.rbstop&&!p_voice.bar_start){p_voice.bar_start=_bar(s) +p_voice.bar_start.bar_type="" +p_voice.bar_start.rbstart=1} +if(s1.text) +xy_str(x+4,y2-gene.curfont.size,s1.text);xypath(x,y2);if(s1.rbstart==2) +output+='m0 10v-10';output+='h'+w.toFixed(1) +if(s.rbstop==2) +output+='v10';output+='"/>\n';y_set(s1.st,true,x,w,y+2) +if(s.rbstart) +s=s.prev}} +for(i=0;i<=nstaff;i++) +minmax[i]={ymin:0,ymax:0} +for(i=0;i'||dd.ty=='@') +continue +f_staff[dd.func](de) +if(dd.func!=6||dd.dd_en) +continue +if((de.pos&C.SL_ALI_MSK)==C.SL_ALIGN||((de.pos&C.SL_ALI_MSK)==0&&de.s.fmt.dynalign>0)){if(de.up){if(de.y>minmax[de.st].ymax) +minmax[de.st].ymax=de.y}else{if(de.y0){y2=y+dd.h+2 +if(y2>staff_tb[de.st].ann_top) +staff_tb[de.st].ann_top=y2}else{y2=y-dd.hd-2 +if(y2'||dd.dd_en) +continue +w=de.val||(dd.wl+dd.wr) +if((de.pos&C.SL_ALI_MSK)==C.SL_ALIGN||((de.pos&C.SL_ALI_MSK)==0&&de.s.fmt.dynalign>0)){if(de.up) +y=minmax[de.st].ymax +else +y=minmax[de.st].ymin;de.y=y}else{y=de.y} +if(de.up) +y+=dd.h;else +y-=dd.hd +y_set(de.st,de.up,de.x,w,y)} +for(i=0;instaff) +return +set_dscale(st) +if(staff_tb[st].staffscale!=1){font_size=get_font("measure").size;param_set_font("measurefont","* "+ +(font_size/staff_tb[st].staffscale).toString())} +set_font("measure");w0=cwidf('0');s=tsfirst;bar_num=gene.nbar +if(bar_num>1){if(cfmt.measurenb==0){any_nb=true;y=y_get(st,true,0,20) +if(y=10) +w*=bar_num>=100?3:2 +if(gene.curfont.pad) +w+=gene.curfont.pad*2 +x=(s.prev?s.prev.x+s.prev.wr/2:s.x-s.wl)-w +y=y_get(st,true,x,w)+5 +if(y=10) +w*=bar_num>=100?3:2 +if(gene.curfont.pad) +w+=gene.curfont.pad*2 +x=s.x +y=y_get(st,true,x,w) +if(y0){if(ynstaff) +return +set_dscale(st,1) +var ymin=staff_tb[st].topbar+2,dosh=0,shift=1,x=-100,yn=0 +for(s=tsfirst;s;s=s.ts_next){s2=s.part +if(!s2||s2.invis) +continue +if(!some_part){some_part=s;set_font("parts");h=gene.curfont.size+2+ +gene.curfont.pad*2} +if(s2.x==undefined) +s2.x=s.x-10 +w=strwh(s2.text)[0] +y=y_get(st,true,s2.x,w+3) +if(yminymin) +ymin=y +if(x>=s.x-16&&!(dosh&(shift>>1))) +dosh|=shift +shift<<=1 +x=s.x-16+w} +if(some_tempo){set_sscale(-1) +set_font("tempo") +h=gene.curfont.size +ymin+=2 +ymin*=staff_tb[st].staffscale +for(s=some_tempo;s;s=s.ts_next){if(s.type!=C.TEMPO||s.invis) +continue +w=s.tempo_wh[0] +y=ymin +if(dosh&1) +y+=h +if(user.anno_start||user.anno_stop){s.wl=16 +s.wr=w-16 +s.ymn=y +s.ymx=s.ymn+14 +anno_start(s)} +writempo(s,s.x-16,y) +anno_stop(s) +y_set(st,1,s.x-16,w,y+h+2) +dosh>>=1}}} +var STEM_MIN=16,STEM_MIN2=14,STEM_MIN3=12,STEM_MIN4=10,STEM_CH_MIN=14,STEM_CH_MIN2=10,STEM_CH_MIN3=9,STEM_CH_MIN4=9,BEAM_DEPTH=3.2,BEAM_OFFSET=.25,BEAM_SHIFT=5,BEAM_STUB=7,SLUR_SLOPE=.5,GSTEM=15,GSTEM_XOFF=2.3 +var cache,anno_a=[] +function b_pos(grace,stem,nflags,b){var top,bot,d1,d2,shift=!grace?BEAM_SHIFT:3.5,depth=!grace?BEAM_DEPTH:1.8 +function rnd6(y){var iy=Math.round((y+12)/6)*6-12 +return iy-y} +if(stem>0){bot=b-(nflags-1)*shift-depth +if(bot>26) +return 0 +top=b}else{top=b+(nflags-1)*shift+depth +if(top<-2) +return 0 +bot=b} +d1=rnd6(top-BEAM_OFFSET);d2=rnd6(bot+BEAM_OFFSET) +return d1*d1>d2*d2?d2:d1} +function sym_dup(s){var m,note +s=clone(s) +s.invis=true +delete s.extra;delete s.text +delete s.a_gch +delete s.a_ly +delete s.a_dd;delete s.tp +s.notes=clone(s.notes) +for(m=0;m<=s.nhd;m++){note=s.notes[m]=clone(s.notes[m]) +delete note.a_dd} +return s} +var min_tb=[[STEM_MIN,STEM_MIN,STEM_MIN2,STEM_MIN3,STEM_MIN4,STEM_MIN4],[STEM_CH_MIN,STEM_CH_MIN,STEM_CH_MIN2,STEM_CH_MIN3,STEM_CH_MIN4,STEM_CH_MIN4]] +Abc.prototype.calculate_beam=function(bm,s1){var s,s2,g,notes,nflags,st,v,two_staves,two_dir,x,y,ys,a,b,stem_err,max_stem_err,p_min,p_max,s_closest,stem_xoff,scale,visible,dy +if(!s1.beam_st){s=sym_dup(s1);lkvsym(s,s1);lktsym(s,s1);s.x-=12 +if(s.x>s1.prev.x+12) +s.x=s1.prev.x+12;s.beam_st=true +delete s.beam_end;s.tmp=true +delete s.sls;s1=s} +notes=nflags=0;two_staves=two_dir=false;st=s1.st;v=s1.v;stem_xoff=s1.grace?GSTEM_XOFF:3.5 +for(s2=s1;;s2=s2.next){if(s2.type==C.NOTE){if(s2.nflags>nflags) +nflags=s2.nflags;notes++ +if(s2.st!=st) +two_staves=true +if(s2.stem!=s1.stem) +two_dir=true +if(!visible&&!s2.invis&&(!s2.stemless||s2.trem2)) +visible=true +if(s2.beam_end) +break} +if(!s2.next){for(;;s2=s2.prev){if(s2.type==C.NOTE) +break} +s=sym_dup(s2);s.next=s2.next +if(s.next) +s.next.prev=s;s2.next=s;s.prev=s2;s.ts_next=s2.ts_next +if(s.ts_next) +s.ts_next.ts_prev=s;s2.ts_next=s;s.ts_prev=s2 +delete s.beam_st;s.beam_end=true;s.tmp=true +delete s.sls;s.x+=12 +if(s.x=0){x=stem_xoff+s.notes[0].shhd +if(s.notes[s.nhd].pit>p_max){p_max=s.notes[s.nhd].pit;s_closest=s}}else{x=-stem_xoff+s.notes[s.nhd].shhd +if(s.notes[0].pit=3&&s_closest!=s1&&s_closest!=s2) +a=0 +y=s1.ys+staff_tb[st].y +if(a==undefined) +a=(s2.ys+staff_tb[s2.st].y-y)/(s2.xs-s1.xs) +if(a!=0){a=s1.fmt.beamslope*a/(s1.fmt.beamslope+Math.abs(a)) +if(a>-.04&&a<.04) +a=0} +b=(y+s2.ys+staff_tb[s2.st].y)/2-a*(s2.xs+s1.xs)/2 +max_stem_err=0;s=s1 +if(two_dir){ys=((s1.grace?3.5:BEAM_SHIFT)*(nflags-1)+ +BEAM_DEPTH)*.5 +if(s1.nflags==s2.nflags);else if(s1.stem!=s2.stem&&s1.nflagss1.xs) +s=s.ts_prev +for(;s&&s.time<=s2.time;s=s.ts_next){if(s.type!=C.NOTE||s.invis||(s.st!=st&&s.v!=v)){continue} +x=s.v==v?s.xs:s.x;ys=a*x+b-staff_tb[s.st].y +if(s.v==v){stem_err=min_tb[s.nhd==0?0:1][s.nflags] +if(s.stem>0){if(s.notes[s.nhd].pit>26){stem_err-=2 +if(s.notes[s.nhd].pit>28) +stem_err-=2} +stem_err-=ys-3*(s.notes[s.nhd].pit-18)}else{if(s.notes[0].pit<18){stem_err-=2 +if(s.notes[0].pit<16) +stem_err-=2} +stem_err-=3*(s.notes[0].pit-18)-ys} +stem_err+=BEAM_DEPTH+BEAM_SHIFT*(s.nflags-1)}else{if(s1.stem>0){if(s.stem>0){if(s.ymn>ys+4||s.ymxv) +stem_err=s.ymx-ys +else +stem_err=s.ymn+8-ys}else{stem_err=s.ymx-ys}}else{if(s.stem<0){if(s.ymxys-beam_h-2) +continue +if(s.vmax_stem_err) +max_stem_err=stem_err}}else{for(;;s=s.next){ys=a*s.xs+b-staff_tb[s.st].y;stem_err=GSTEM-2 +if(s.stem>0) +stem_err-=ys-(3*(s.notes[s.nhd].pit-18)) +else +stem_err+=ys-(3*(s.notes[0].pit-18));stem_err+=3*(s.nflags-1) +if(stem_err>max_stem_err) +max_stem_err=stem_err +if(s==s2) +break}} +if(max_stem_err>0) +b+=s1.stem*max_stem_err +if(!two_staves&&!two_dir) +for(s=s1.next;;s=s.next){switch(s.type){case C.REST:if(!s.multi) +break +g=s.ts_next +if(!g||g.st!=st||(g.type!=C.NOTE&&g.type!=C.REST)) +break +if(s.invis) +break +case C.CLEF:y=a*s.x+b +if(s1.stem>0){y=s.ymx-y ++BEAM_DEPTH+BEAM_SHIFT*(nflags-1) ++2 +if(y>0) +b+=y}else{y=s.ymn-y +-BEAM_DEPTH-BEAM_SHIFT*(nflags-1) +-2 +if(y<0) +b+=y} +break +case C.GRACE:for(g=s.extra;g;g=g.next){y=a*g.x+b +if(s1.stem>0){y=g.ymx-y ++BEAM_DEPTH+BEAM_SHIFT*(nflags-1) ++2 +if(y>0) +b+=y}else{y=g.ymn-y +-BEAM_DEPTH-BEAM_SHIFT*(nflags-1) +-2 +if(y<0) +b+=y}} +break} +if(s==s2) +break} +if(a==0) +b+=b_pos(s1.grace,s1.stem,nflags,b-staff_tb[st].y) +for(s=s1;;s=s.next){switch(s.type){case C.NOTE:s.ys=a*s.xs+b-staff_tb[s.st].y +if(s.stem>0){s.ymx=s.ys+2.5 +if(s.ts_prev&&s.ts_prev.stem>0&&s.ts_prev.st==s.st&&s.ts_prev.ymn0){y-=dy +if(s1.multi==0&&y>12) +y=12 +if(s.y<=y) +break}else{y+=dy +if(s1.multi==0&&y<12) +y=12 +if(s.y>=y) +break} +if(s.head!=C.FULL) +y=(((y+3+12)/6)|0)*6-12;s.y=y +break} +if(s==s2) +break} +if(staff_tb[st].y==0) +return false +bm.s1=s1;bm.a=a;bm.b=b;bm.nflags=nflags +return true} +function draw_beams(bm){var s,i,beam_dir,shift,bshift,bstub,bh,da,bd,k,k1,k2,x1,s1=bm.s1,s2=bm.s2 +function draw_beam(x1,x2,dy,h,bm,n){var y1,dy2,s=bm.s1,nflags=s.nflags +if(s.ntrem) +nflags-=s.ntrem +if(s.trem2&&n>nflags){if(s.dur>=C.BLEN/2){x1=s.x+6;x2=bm.s2.x-6}else if(s.dur\n'} +anno_start(s1,'beam') +if(!s1.grace){bshift=BEAM_SHIFT;bstub=BEAM_STUB;shift=.34;bh=BEAM_DEPTH}else{bshift=3.5;bstub=3.2;shift=.29;bh=1.8} +bh/=stv_g.scale +beam_dir=s1.stem +if(s1.stem!=s2.stem&&s1.nflags0){da=-da;bshift=da*s1.xs}else{bshift=da*s2.xs} +da=da*beam_dir} +shift=0 +for(i=2;i<=bm.nflags;i++){shift+=bshift +if(da!=0) +bm.a+=da +for(s=s1;;s=s.next){if(s.type!=C.NOTE||s.nflagss.nflags-s.ntrem){x1=(s.dur>=C.BLEN/2)?s.x:s.xs;draw_beam(x1-5,x1+5,(shift+2.5)*beam_dir,bh,bm,i) +if(s==s2) +break +continue} +k1=s +while(1){if(s==s2) +break +k=s.next +if(k.type==C.NOTE||k.type==C.REST){if(k.trem1){if(k.nflags-k.ntrem2)) +break +s=k} +k2=s +while(k2.type!=C.NOTE) +k2=k2.prev;x1=k1.xs +bd=beam_dir +if(k1==k2){if(k1==s1){x1+=bstub}else if(k1==s2){x1-=bstub}else if(k1.beam_br1||(k1.beam_br2&&i>2)){x1+=bstub}else{k=k1.next +while(k.type!=C.NOTE) +k=k.next +if(k.beam_br1||(k.beam_br2&&i>2)){x1-=bstub}else{k1=k1.prev +while(k1.type!=C.NOTE) +k1=k1.prev +if(k1.nflagsi;j--){if(cur_sy.st_print[j]) +break} +if(i==j&&l==0) +return +yb=staff_tb[j].y+staff_tb[j].botbar*staff_tb[j].staffscale;h=staff_tb[i].y+staff_tb[i].topbar*staff_tb[i].staffscale-yb;xypath(x,yb);output+="v"+(-h).toFixed(1)+'"/>\n' +for(i=0;i<=nst;i++){fl=cur_sy.staves[i].flags +if(fl&OPEN_BRACE) +draw_sysbra(x,i,CLOSE_BRACE) +if(fl&OPEN_BRACKET) +draw_sysbra(x,i,CLOSE_BRACKET) +if(fl&OPEN_BRACE2) +draw_sysbra(x-6,i,CLOSE_BRACE2) +if(fl&OPEN_BRACKET2) +draw_sysbra(x-6,i,CLOSE_BRACKET2)}} +function draw_meter(s){if(!s.a_meter) +return +var dx,i,j,meter,x,st=s.st,p_staff=staff_tb[st],y=p_staff.y;if(p_staff.stafflines!='|||||') +y+=(p_staff.topbar+p_staff.botbar)/2-12 +for(i=0;i\n\ + A\n\ + B\n\ +\n',x,y+6,m_gl(meter.top),m_gl(meter.bot))}else{out_XYAB('\ +A\n',x,y+12,m_gl(meter.top))}}} +var acc_nd={} +function draw_acc(x,y,a){if(typeof a=="object"){var c,n=a[0],d=a[1] +c=n+'_'+d +a=acc_nd[c] +if(!a){a=abc2svg.rat(Math.abs(n),d) +d=a[1] +a=(n<0?-a[0]:a[0]).toString() +if(d!=1) +a+='_'+d +acc_nd[c]=a}} +xygl(x,y,"acc"+a)} +function set_hl(p_st,n,x,dx1,dx2){var i,hl +if(n>=0){hl=p_st.hlu[n] +if(!hl) +hl=p_st.hlu[n]=[]}else{hl=p_st.hld[-n] +if(!hl) +hl=p_st.hld[-n]=[]} +for(i=0;i=hl[i][0]) +break} +if(i==hl.length){hl.push([x,dx1,dx2])}else if(x>hl[i][0]){hl.splice(++i,0,[x,dx1,dx2])}else{if(dx1hl[i][2]) +hl[i][2]=dx2}} +Abc.prototype.draw_hl=function(s){var i,j,n,note,hla=[],st=s.st,p_staff=staff_tb[st] +if(!p_staff.hll||s.invis) +return +for(i=0;i<=s.nhd;i++){note=s.notes[i] +if(!p_staff.hlmap[note.pit-p_staff.hll]) +hla.push([note.pit-18,note.shhd*stv_g.scale])} +n=hla.length +if(!n) +return +var dx1,dx2,hl,shhd,hlp,stafflines=p_staff.stafflines,top=stafflines.length-1,yu=top,bot=p_staff.botline/6,yl=bot,dx=s.grace?4:hw_tb[s.head]*1.3 +note=s.notes[s.stem<0?s.nhd:0] +shhd=note.shhd +for(i=0;ishhd?hla[i][1]:shhd)+dx +if(hlp>1 +n--}else if(hlp>top*2){yu=hlp>>1 +n--} +set_hl(p_staff,hlp>>1,s.x,dx1,dx2)} +dx1=shhd-dx +dx2=shhd+dx +while(++yltop) +set_hl(p_staff,yu,s.x,dx1,dx2) +if(!n) +return +i=yl;j=yu +while(i>bot&&stafflines[i]=='-') +i-- +while(j0){for(nacc=0;nacc9?sharp1:sharp2 +for(i=0;iold_sf;i--){xygl(x,staffb+shift,"acc3");shift+=p_seq[-i];x+=5.5} +if(s.k_sf!=0) +x+=3}} +if(s.k_sf>0){shift=sharp_cl[clef_ix];p_seq=shift>9?sharp1:sharp2 +for(i=0;is.k_sf;i--){xygl(x,staffb+shift,"acc-1");shift+=p_seq[-i];x+=5.5} +if(s.fmt.cancelkey&&i>old_sf){x+=2 +for(;i>old_sf;i--){xygl(x,staffb+shift,"acc3");shift+=p_seq[-i];x+=5.5}}}}else if(a_acc.length){var acc,last_acc=a_acc[0].acc,last_shift=100,s2={st:st,nhd:0,notes:[{}]} +for(i=0;i27) +shift-=21 +if(i!=0&&(shift>last_shift+18||shift2&&s.v==cur_sy.top_voice&&s.fmt.measrepnb>0&&!(s.rep_nb%s.fmt.measrepnb)) +nrep_out(x,yb+p_staff.topbar,s.rep_nb)} +anno_a.push(s) +return} +set_scale(s);anno_start(s);if(s.notes[0].color) +set_color(s.notes[0].color);y=s.y;i=5-s.nflags +if(i==7&&y==12&&p_staff.stafflines.length<=2) +y-=6 +if(!s.notes[0].invis) +xygl(x,y+yb,rest_tb[i]) +if(s.dots){x+=8;y+=yb+3 +j=s.dots +i=(s.dur_orig/12)>>((5-s.nflags)-j) +while(j-->0){xygl(x,y,(i&(1<>2 +if(n&3){k++ +if(n&3==3) +k++} +x-=3*(k-1) +while(n>=4){xygl(x,y,"r00") +n-=4 +x+=6} +if(n>=2){xygl(x,y,"r0") +n-=2 +x+=6} +if(n) +xygl(x+2,y,"r1")} +if(!s.next){error(1,s,"Lack of bar after multi-measure rest") +return} +set_scale(s) +prev=s +while(!prev.seqst) +prev=prev.ts_prev +prev=prev.ts_prev +while(!prev.seqst) +prev=prev.ts_prev +x1=prev.x+20 +x2=s.next.x-20 +s.x=(x1+x2)/2 +anno_start(s) +if(!cfmt.oldmrest||s.nmes>cfmt.oldmrest){out_XYAB('\n'}else{omrest()} +if(s.tacet) +out_XYAB('A\n',s.x,y+18,s.tacet) +else +out_XYAB('A\n',s.x,y+22,m_gl(p)) +anno_a.push(s)} +function grace_slur(s){var yy,x0,y0,x3,y3,bet1,bet2,dy1,dy2,last,below,so=s,g=s.extra +while(1){if(!g.next) +break +g=g.next} +last=g +below=((g.stem>=0||s.multi<0)&&g.notes[0].pit<=28)||g.notes[0].pit<16 +if(below){yy=127 +for(g=s.extra;g;g=g.next){if(g.y-2) +x3-=4;y3=3*(s.notes[0].pit-18)-5;dy1=(x3-x0)*.4 +if(dy1>3) +dy1=3;dy2=dy1;bet1=.2;bet2=.8 +if(y0>y3+7){x0=last.x-1;y0+=.5;y3+=6.5;x3=s.x-5.5;dy1=(y0-y3)*.8;dy2=(y0-y3)*.2;bet1=0}else if(y3>y0+4){y3=y0+4;x0=last.x+2;y0=last.y-4}}else{yy=-127 +for(g=s.extra;g;g=g.next){if(g.y>yy){yy=g.y;last=g}} +x0=last.x;y0=last.y+5 +if(s.extra!=last){x0-=4;y0-=1} +s=s.next;x3=s.x-1 +if(s.stem>=0&&s.nflags>-2) +x3-=2;y3=3*(s.notes[s.nhd].pit-18)+5;dy1=(x0-x3)*.4 +if(dy1<-3) +dy1=-3;dy2=dy1;bet1=.2;bet2=.8 +if(y0g.ymx) +g.ymx=y0}} +function draw_gracenotes(s){var x1,y1,last,note,bm={},g=s.extra +while(1){if(g.beam_st&&!g.beam_end){if(self.calculate_beam(bm,g)) +draw_beams(bm)} +anno_start(g) +draw_note(g,!bm.s2) +if(g==bm.s2) +bm.s2=null +anno_a.push(s) +if(!g.next) +break +g=g.next} +last=g +if(s.sappo){g=s.extra +if(!g.next){x1=9 +y1=g.stem>0?5:-5}else{x1=(g.next.x-g.x)*.5+4 +y1=(g.ys+g.next.ys)*.5-g.y +if(g.stem>0) +y1-=1 +else +y1+=1} +note=g.notes[g.stem<0?0:g.nhd] +out_acciac(x_head(g,note),y_head(g,note),x1,y1,g.stem>0)} +g=s.slur +if(g){anno_start(s,'slur') +xypath(g.x0,g.y0+staff_tb[s.st].y) +output+='c'+g.x1.toFixed(1)+' '+g.y1.toFixed(1)+' '+g.x2.toFixed(1)+' '+g.y2.toFixed(1)+' '+g.x3.toFixed(1)+' '+g.y3.toFixed(1)+'"/>\n' +anno_stop(s,'slur')}} +function setdoty(s,y_tb){var m,m1,y +for(m=0;m<=s.nhd;m++){y=3*(s.notes[m].pit-18) +if((y%6)==0){if(s.dot_low) +y-=3 +else +y+=3} +y_tb[m]=y} +for(m=0;my_tb[m]) +continue +m1=m +while(m1>0){if(y_tb[m1]>y_tb[m1-1]+6) +break +m1--} +if(3*(s.notes[m1].pit-18)-y_tb[m1]=0){if(s.stem>=0) +p=p.slice(0,i) +else +p=p.slice(i+1)}}else if(s.type==C.CUSTOS){p="custos"}else{switch(head){case C.OVAL:p="HD" +break +case C.OVALBARS:if(s.head!=C.SQUARE){p="HDD" +break} +case C.SQUARE:if(nflags>-4){p="breve"}else{p="longa" +inv=s.stem>0} +if(!tsnext&&s.next&&s.next.type==C.BAR&&!s.next.next) +dots=0 +x_note+=1 +break +case C.EMPTY:p="Hd" +break +default:p="hd" +break}} +if(note.color!=undefined) +old_color=set_color(note.color) +if(p){if(inv){g_open(x_note,y_note,0,1,-1);x_note=y_note=0} +if(!self.psxygl(x_note,y_note,p)) +xygl(x_note,y_note,p) +if(inv) +g_close()} +if(dots){dotx=x+(7.7+s.xmx)*stv_g.scale +if(y_tb[m]==undefined){y_tb[m]=3*(s.notes[m].pit-18) +if((s.notes[m].pit&1)==0) +y_tb[m]+=3} +doty=y_tb[m]+staffb +i=(note.dur/12)>>((5-nflags)-dots) +while(dots-->0){xygl(dotx,doty,(i&(1<0){if(s.stem>=0) +slen-=1 +else +slen+=1} +out_stem(x,y,slen,s.grace)}else{out_stem(x,y,slen,s.grace,nflags,s.fmt.straightflags)}}else if(s.xstem){s2=s.ts_prev;slen=(s2.stem>0?s2.y:s2.ys)-s.y;slen+=staff_tb[s2.st].y-staffb;out_stem(x,y,slen)} +if(fl&&s.trem1){var ntrem=s.ntrem||0,x1=x;slen=3*(s.notes[s.stem>0?s.nhd:0].pit-18) +if(s.head==C.FULL||s.head==C.EMPTY){x1+=(s.grace?GSTEM_XOFF:3.5)*s.stem +if(s.stem>0) +slen+=6+5.4*ntrem +else +slen-=6+5.4}else{if(s.stem>0) +slen+=5+5.4*ntrem +else +slen-=5+5.4} +slen/=s.p_v.scale;out_trem(x1,staffb+slen,ntrem)} +for(m=0;m<=s.nhd;m++) +draw_basic_note(s,m,y_tb)} +function prev_scut(s){while(s.prev){s=s.prev +if(s.rbstart) +return s} +s=s.p_v.sym +while(s.type!=C.CLEF) +s=s.ts_prev +if(s.next&&s.next.type==C.KEY) +s=s.next +if(s.next&&s.next.type==C.METER) +return s.next +return s} +function slur_direction(k1,k2){var s,some_upstem,low,dir +function slur_multi(s1,s2){if(s1.multi) +return s1.multi +if(s2.multi) +return s2.multi +return 0} +if(k1.grace&&k1.stem>0) +return-1 +dir=slur_multi(k1,k2) +if(dir) +return dir +for(s=k1;;s=s.next){if(s.type==C.NOTE){if(!s.stemless){if(s.stem<0) +return 1 +some_upstem=true} +if(s.notes[0].pit<22) +low=true} +if(s.time==k2.time) +break} +if(!some_upstem&&!low) +return 1 +return-1} +function slur_out(x1,y1,x2,y2,dir,height,dotted){var dx,dy,dz,alfa=.3,beta=.45;dy=y2-y1 +if(dy<0) +dy=-dy;dx=x2-x1 +if(dx>40.&&dy/dx<.7){alfa=.3+.002*(dx-40.) +if(alfa>.7) +alfa=.7} +var mx=.5*(x1+x2),my=.5*(y1+y2),xx1=mx+alfa*(x1-mx),yy1=my+alfa*(y1-my)+height;xx1=x1+beta*(xx1-x1);yy1=y1+beta*(yy1-y1) +var xx2=mx+alfa*(x2-mx),yy2=my+alfa*(y2-my)+height;xx2=x2+beta*(xx2-x2);yy2=y2+beta*(yy2-y2);dy=2*dir;dz=.2+.001*dx +if(dz>.6) +dz=.6;dz*=dir +dx*=.03 +var scale_y=1 +if(!dotted) +output+='\n'} +function draw_slur(path,sl,recurr){var i,k,g,x1,y1,x2,y2,height,addy,s_st2,a,y,z,h,dx,dy,ty=sl.ty,dir=(ty&0x07)==C.SL_ABOVE?1:-1,n=path.length,i1=0,i2=n-1,not1=sl.nts,k1=path[0],k2=path[i2],nn=1 +set_dscale(k1.st) +for(i=1;ik1.st) +h=-h +for(i=0;ih) +h=a.ymx}}else{for(i1=1;i1h) +h=a.ymx} +for(i1=i;i1s_st2.st?'/':'\\' +if(sl.ty&C.SL_CENTER) +ty=ty+ty +else if(k1.st==k2.st) +ty=ty=='/'?'/\\':'\\/' +else +ty+=dir>0?'+':'-' +var savout=output +output="" +draw_slur(path,sl,1) +gene.a_sl.push([k1,s_st2,ty,output]) +output=savout +return} +x1=k1.x +if(k1.notes&&k1.notes[0].shhd) +x1+=k1.notes[0].shhd;x2=k2.x +if(k2.notes) +x2+=k2.notes[0].shhd +if(not1){y1=3*(not1.pit-18)+2*dir +x1+=3}else{y1=dir>0?k1.ymx+2:k1.ymn-2 +if(k1.type==C.NOTE){if(dir>0){if(k1.stem>0){x1+=5 +if(k1.beam_end&&k1.nflags>=-1&&!k1.in_tuplet){if(k1.nflags>0){x1+=2;y1=k1.ys-3}else{y1=k1.ys-6}}else{y1=k1.ys+3}}else{y1=k1.y+8}}else{if(k1.stem<0){x1-=1 +if(k2.grace){y1=k1.y-8}else if(k1.beam_end&&k1.nflags>=-1&&(!k1.in_tuplet||k1.ys0){x1+=2;y1=k1.ys+3}else{y1=k1.ys+6}}else{y1=k1.ys-3}}else{y1=k1.y-8}}}} +if(sl.nte){y2=3*(sl.nte.pit-18)+2*dir +x2-=3}else{y2=dir>0?k2.ymx+2:k2.ymn-2 +if(k2.type==C.NOTE){if(dir>0){if(k2.stem>0){x2+=1 +if(k2.beam_st&&k2.nflags>=-1&&!k2.in_tuplet) +y2=k2.ys-6 +else +y2=k2.ys+3}else{y2=k2.y+8}}else{if(k2.stem<0){x2-=5 +if(k2.beam_st&&k2.nflags>=-1&&!k2.in_tuplet) +y2=k2.ys+6 +else +y2=k2.ys-3}else{y2=k2.y-8}}}} +if(k1.type!=C.NOTE){y1=y2+1.2*dir;x1=k1.x+k1.wr*.5 +if(x1>x2-12) +x1=x2-12} +if(k2.type!=C.NOTE){if(k1.type==C.NOTE) +y2=y1+1.2*dir +else +y2=y1 +if(k1!=k2) +x2=k2.x-k2.wl*.3} +if(nn>=3){k=path[1] +if(k.type!=C.BAR&&k.x0){y=k.ymx-2 +if(y1y) +y1=y}} +k=path[i2-1] +if(k.type!=C.BAR&&k.x>x2-48){if(dir>0){y=k.ymx-2 +if(y2y) +y2=y}}} +a=(y2-y1)/(x2-x1) +if(a>SLUR_SLOPE||a<-SLUR_SLOPE){a=a>SLUR_SLOPE?SLUR_SLOPE:-SLUR_SLOPE +if(a*dir>0) +y1=y2-a*(x2-x1) +else +y2=y1+a*(x2-x1)} +y=y2-y1 +if(y>8) +y=8 +else if(y<-8) +y=-8 +z=y +if(z<0) +z=-z;dx=.5*z;dy=.3*y +if(y*dir>0){x2-=dx;y2-=dy}else{x1+=dx;y1+=dy} +if(k1.grace) +x1=k1.x-GSTEM_XOFF*.5 +if(k2.grace) +x2=k2.x+GSTEM_XOFF*1.5;h=0;a=(y2-y1)/(x2-x1) +if(k1!=k2&&k1.v==k2.v){addy=y1-a*x1 +for(i=1;i0){y=3*(k.notes[k.nhd].pit-18)+6 +if(yh) +h=y}else{y=3*(k.notes[0].pit-18)-6 +if(y>k.ymn) +y=k.ymn;y-=a*k.x+addy +if(y0){y=3*(g.notes[g.nhd].pit-18)+6 +if(yh) +h=y}else{y=3*(g.notes[0].pit-18)-6 +if(y>g.ymn) +y=g.ymn;y-=a*g.x+addy +if(y3) +height=(.08*(x2-x1)+12)*dir +else +height=(.03*(x2-x1)+8)*dir +if(dir>0){if(height<3*h) +height=3*h +if(height>40) +height=40}else{if(height>3*h) +height=3*h +if(height<-40) +height=-40} +y=y2-y1 +if(y<0) +y=-y +if(dir>0){if(height<.8*y) +height=.8*y}else{if(height>-.8*y) +height=-.8*y} +height*=k1.fmt.slurheight;slur_out(x1,y1,x2,y2,dir,height,ty&C.SL_DOTTED);dx=x2-x1;a=(y2-y1)/dx;addy=y1-a*x1 +if(height>0) +addy+=4*Math.sqrt(height)-2 +else +addy-=4*Math.sqrt(-height)-2 +for(i=0;iy) +k.ymn=y +if(recurr) +continue +if(i==i2-1){dx=x2 +if(sl.nte) +dx-=5}else{dx=k.x+k.wr} +if(i!=0) +x1=k.x +if(!i||i==i2) +y-=height/3 +dx-=x1-k.wl +y_set(k1.st,dir>0,x1-k.wl,dx,y)}} +function draw_slurs(s,last){var gr1,i,m,note,sls,nsls +function draw_sls(s,sl){var k,v,i,dir,s3,path=[],s2=sl.se +if(last&&s2.time>last.time) +return +switch(sl.loc){case'i':s=prev_scut(s) +break +case'o':for(s3=s;s3.ts_next;s3=s3.ts_next);s2=s3 +for(;s3;s3=s3.ts_prev){if(s3.v==s.v){s2=s3 +break} +if(s3.st==s.st) +s2=s3 +if(s3.ts_prev.time!=s2.time) +break} +break} +if(s.p_v.s_next&&s2.time>=tsnext.time){if(s2.time==tsnext.time){if(s2.grace){for(s3=tsnext;s3&&s3.time==s2.time;s3=s3.ts_next){if(s3.type==C.GRACE){s3=null +break}}}else{for(s3=tsnext;s3.time==s2.time;s3=s3.ts_next){if(s3==s2){s3=null +break}}}}else{s3=null} +if(!s3){s.p_v.sls.push(sl);s2=s.p_v.s_next.prev +while(s2.next) +s2=s2.next;sl=Object.create(sl)}} +switch(sl.ty&0x07){case C.SL_ABOVE:dir=1;break +case C.SL_BELOW:dir=-1;break +default:dir=s.v!=s2.v?1:slur_direction(s,s2) +sl.ty&=~0x07 +sl.ty|=dir>0?C.SL_ABOVE:C.SL_BELOW +break} +if(s.v==s2.v){v=s.v}if(!cur_sy.voices[s.v]||!cur_sy.voices[s2.v]){v=s.v>s2.v?s.v:s2.v}else if(dir*(cur_sy.voices[s.v].range<=cur_sy.voices[s2.v].range?1:-1)>0) +v=s.v +else +v=s2.v +if(gr1&&!(s2.grace&&s.v==s2.v&&s.time==s2.time)){do{path.push(s);s=s.next}while(s);s=gr1.next}else{path.push(s);if(s.grace) +s=s.next +else +s=s.ts_next} +if(!s2.grace){while(s){if(s.v==v) +path.push(s) +if(s==s2) +break +s=s.ts_next}}else if(s.grace){while(1){path.push(s) +if(s==s2) +break +s=s.next}}else{k=s2 +while(k.prev) +k=k.prev +while(1){if(s.v==v) +path.push(s) +if(s.extra==k) +break +s=s.ts_next} +s=k +while(1){path.push(s) +if(s==s2) +break +s=s.next}} +for(i=1;istd){std=s2.st} +if(s2.tp) +draw_tuplet(s2) +if(s2.tpe) +break} +if(s2) +s2.tpe-- +if(tp.f[0]==1) +return +if(!s2){error(1,s1,"No end of tuplet in this music line") +return} +dir=tp.f[3] +if(!dir){s3=s1 +while(s3&&!s3.stem) +s3=s3.next +dir=(s3&&s3.stem<0)?C.SL_BELOW:C.SL_ABOVE} +set_dscale(dir==C.SL_ABOVE?stu:std) +if(s1==s2||tp.f[1]==2){nb_only=true}else if(tp.f[1]==1){nb_only=true;draw_slur([s1,s2],{ty:dir})}else{if(tp.f[0]!=2&&s1.type==C.NOTE&&s2.type==C.NOTE){nb_only=true +for(s3=s1;;s3=s3.next){if(s3.type!=C.NOTE&&s3.type!=C.REST){if(s3.type==C.GRACE||s3.type==C.SPACE) +continue +nb_only=false +break} +if(s3==s2) +break +if(s3.beam_end){nb_only=false +break}} +if(nb_only&&!s1.beam_st&&!s1.beam_br1&&!s1.beam_br2){for(s3=s1.prev;s3;s3=s3.prev){if(s3.type==C.NOTE||s3.type==C.REST){if(s3.nflags>=s1.nflags) +nb_only=false +break}}} +if(nb_only&&!s2.beam_end){for(s3=s2.next;s3;s3=s3.next){if(s3.type==C.NOTE||s3.type==C.REST){if(!s3.beam_br1&&!s3.beam_br2&&s3.nflags>=s2.nflags) +nb_only=false +break}}}}} +if(nb_only){if(tp.f[2]==1) +return +set_font("tuplet") +xm=(s2.x+s1.x)/2 +if(dir==C.SL_ABOVE) +ym=y_get(stu,1,xm-4,8) +else +ym=y_get(std,0,xm-4,8)- +gene.curfont.size +if(s1.stem*s2.stem>0){if(s1.stem>0) +xm+=4 +else +xm-=4} +yy=ym+gene.curfont.size*.22 +if(tp.f[2]==0) +xy_str(xm,yy,tp.p.toString(),'c') +else +xy_str(xm,yy,tp.p+':'+tp.q,'c') +for(s3=s1;;s3=s3.next){if(s3.x>=xm) +break} +if(dir==C.SL_ABOVE){ym+=gene.curfont.size +if(s3.ymxym) +s3.ymn=ym;y_set(std,0,xm-3,6,ym)} +return} +x1=s1.x-4 +if(s2.dur>s2.prev.dur){s3=s2.next +if(!s3||s3.time!=s2.time+s2.dur){for(s3=s2.ts_next;s3;s3=s3.ts_next){if(s3.seqst&&s3.time>=s2.time+s2.dur) +break}} +x2=s3?s3.x-s3.wl-5:realwidth-6}else{x2=s2.x+4 +r=s2.stem>=0?0:s2.nhd +if(s2.notes[r].shhd>0) +x2+=s2.notes[r].shhd +if(s2.st==stu&&s2.stem>0) +x2+=3.5} +if(dir==C.SL_ABOVE){if(s1.st>=s2.st){if(s1.stem>0) +x1+=3 +ym=y_get(s1.st,1,x1-4,8) +y1=ym>staff_tb[s1.st].topbar+2?ym:staff_tb[s1.st].topbar+2}else{y1=staff_tb[s1.st].topbar+2} +if(s2.st>=s1.st){ym=y_get(s2.st,1,x2-4,8) +y2=ym>staff_tb[s2.st].topbar+2?ym:staff_tb[s2.st].topbar+2}else{y2=staff_tb[s2.st].topbar+2} +xm=.5*(x1+x2);ym=.5*(y1+y2);a=(y2-y1)/(x2-x1);s0=3*(s2.notes[s2.nhd].pit-s1.notes[s1.nhd].pit)/(x2-x1) +if(s0>0){if(a<0) +a=0 +else if(a>s0) +a=s0}else{if(a>0) +a=0 +else if(ady) +dy=yx-yy +if(s3==s2) +break} +ym+=dy;y1=ym+a*(x1-xm);y2=ym+a*(x2-xm);ym+=6 +for(s3=s1;;s3=s3.next){if(s3.st==stu){yy=ym+(s3.x-xm)*a +if(s3.ymx0){if(a<0) +a=0 +else if(a>s0) +a=s0 +if(a>.35) +a=.35}else{if(a>0) +a=0 +else if(ayy) +s3.ymn=yy;y_set(std,0,s3.x-3,6,yy)} +if(s3==s2) +break}} +if(tp.f[2]==1){out_tubr(x1,y1+4,x2-x1,y2-y1,dir==C.SL_ABOVE);return} +out_tubrn(x1,y1,x2-x1,y2-y1,dir==C.SL_ABOVE,tp.f[2]==0?tp.p.toString():tp.p+':'+tp.q);if(dir==C.SL_ABOVE) +y_set(stu,1,xm-3,6,yy+2) +else +y_set(std,0,xm-3,6,yy)} +function draw_tie(not1,not2,job){var m,x1,s,y,h,time,p=job==2?not1.pit:not2.pit,dir=(not1.tie_ty&0x07)==C.SL_ABOVE?1:-1,s1=not1.s,st=s1.st,s2=not2.s,x2=s2.x,sh=not1.shhd +for(m=0;m0){if(msh) +sh=s1.notes[m+1].shhd}else{if(m>0&&p==s1.notes[m-1].pit+1) +if(s1.notes[m-1].shhd>sh) +sh=s1.notes[m-1].shhd} +x1=s1.x+sh +if(job!=2){for(m=0;m0){if(m0&&p==s2.notes[m-1].pit+1) +if(s2.notes[m-1].shhdx2-20) +x1=x2-20 +break +case 2:x2=s1.next?s1.next.x:realwidth +if(x2!=realwidth) +x2-=(x2-x1)*.4 +if(x220){x1+=3.5 +x2-=3.5}else{x1+=1.5 +x2-=1.5} +if(s1.dots&&!(not1.pit&1)&&((dir>0&&!s1.dot_low)||(dir<0&&s1.dot_low))) +x1+=5 +y=staff_tb[st].y+3*(p-18)+dir +h=(.03*(x2-x1)+16)*dir*s1.fmt.tieheight +slur_out(x1,y,x2,y,dir,h,not1.tie_ty&C.SL_DOTTED)} +function draw_all_ties(p_voice){var s,s1,s2,clef_chg,x,dx,m,not1,not2,tim2=0 +s1=p_voice.sym +set_color(s1.color) +for(;s1;s1=s1.next){if(s1.ti2&&s1.time!=tim2){for(m=0;m<=s1.nhd;m++){not2=s1.notes[m] +not1=not2.tie_s +if(!not1||not1.s.v!=s1.v) +continue +draw_tie(not1,not2,1)}} +if(!s1.ti1) +continue +if(s1.type==C.GRACE){for(s=s1.extra;s;s=s.next){for(m=0;m<=s1.nhd;m++){not1=s.notes[m] +not2=not1.tie_e +if(!not2) +continue +draw_tie(not1,not2) +tim2=not2.s.time}} +continue} +for(m=0;m<=s1.nhd;m++){not1=s1.notes[m] +not2=not1.tie_e +if(!not2){if(not1.tie_ty) +draw_tie(not1,not1,2) +continue} +s2=not2.s +if(tsnext&&s2.time>=tsnext.time){draw_tie(not1,not2,2) +continue} +tim2=s2.time +for(s=s1.ts_next;s!=s2;s=s.ts_next){if(s.st!=s1.st) +continue +if(s.type==C.CLEF){clef_chg=true +break}} +if(clef_chg||s1.st!=s2.st){draw_tie(not1,not2,2) +draw_tie(not1,not2,3) +clef_chg=false}else{draw_tie(not1,not2)}}}} +function draw_sym_near(){var p_voice,p_st,s,v,st,y,g,w,i,st,dx,top,bot,ymn,output_sav=output;function set_yab(s1,s2){var y,k=realwidth/YSTEP,i=(s1.x/k)|0,j=(s2.x/k)|0,a=(s1.ys-s2.ys)/(s1.xs-s2.xs),b=s1.ys-s1.xs*a,p_st=staff_tb[s1.st] +k*=a +if(s1.stem>0){while(i<=j){y=k*i+b +if(p_st.top[i]y) +p_st.bot[i]=y +i++}}} +output="" +YSTEP=Math.ceil(realwidth/2) +for(st=0;st<=nstaff;st++){p_st=staff_tb[st] +p_st.top=new Float32Array(YSTEP) +p_st.bot=new Float32Array(YSTEP) +for(i=0;i0){if(s.stemless){dx=-5;w=10}else if(s.beam_st){dx=3;w=s.beam_end?4:10}else{dx=-8;w=s.beam_end?11:16} +y_set(s.st,true,s.x+dx,w,s.ymx);ymn=s.ymn +if(s.notes[0].acc&&ymn>3*(s.notes[0].pit-18)-9) +ymn=3*(s.notes[0].pit-18)-9 +y_set(s.st,false,s.x-s.wl,s.wl+s.wr,ymn)}else{y_set(s.st,true,s.x-s.wl,s.wl+s.wr,s.ymx);if(s.stemless){dx=-5;w=10}else if(s.beam_st){dx=-6;w=s.beam_end?4:10}else{dx=-8;w=s.beam_end?5:16} +dx+=s.notes[0].shhd;y_set(s.st,false,s.x+dx,w,s.ymn)} +if(s.notes[s.nhd].acc){y=3*(s.notes[s.nhd].pit-18) ++(s.notes[s.nhd].acc==-1?11:10) +y_set(s.st,true,s.x-10,10,y)} +if(s.notes[0].acc){y=3*(s.notes[0].pit-18) +-(s.notes[0].acc==-1?5:10) +y_set(s.st,false,s.x-10,10,y)}} +draw_deco_note() +for(v=0;vp_st.top[i]) +p_st.top[i]=top +if(bot=0) +draw_measnb();set_dscale(-1) +for(v=0;v=0;){if(stl[st]) +break} +if(st<0) +return +for(v=0;vnstaff){st--;p_staff=staff_tb[st]} +p_staff=staff_tb[st] +for(i=0;imaxsep) +dy=maxsep;y+=dy;p_staff.y=-y;while(!gene.st_print[++prev_staff]) +staff_tb[prev_staff].y=-y +while(1){sy_staff_prev=sy.staves[prev_staff] +if(sy_staff_prev) +break +sy=sy.next}} +mbot=0 +for(i=0;ival) +mbot=val} +if(mbot>p_staff.ann_bot) +mbot=p_staff.ann_bot;mbot*=staff_tb[prev_staff].staffscale +for(st=0;st<=nstaff;st++){p_staff=staff_tb[st];dy=p_staff.y +if(p_staff.staffscale!=1){p_staff.scale_str='transform="translate(0,'+ +(posy-dy).toFixed(1)+') '+'scale('+p_staff.staffscale.toFixed(2)+')"'}} +if(mbot==0){for(st=nstaff;st>=0;st--){if(gene.st_print[st]) +break} +if(st<0) +return y} +dy=-mbot;staffsep=fmt.staffsep*.5 +if(dymaxsep) +dy=maxsep;return y+dy} +function draw_systems(indent){var s,s2,st,x,x2,res,sy,xstaff=[],stl=[],bar_bot=[],bar_height=[],ba=[],sb="",thb="" +function bar_set(){var st,staffscale,top,bot,dy=0 +for(st=0;st<=cur_sy.nstaff;st++){if(xstaff[st]<0){bar_bot[st]=bar_height[st]=0 +continue} +staffscale=staff_tb[st].staffscale;top=staff_tb[st].topbar*staffscale;bot=staff_tb[st].botbar*staffscale +if(dy==0) +dy=staff_tb[st].y+top;bar_bot[st]=staff_tb[st].y+bot;bar_height[st]=dy-bar_bot[st];dy=(cur_sy.staves[st].flags&STOP_BAR)?0:bar_bot[st]}} +function draw_staff(st,x1,x2){var w,i,dy,ty,y=0,ln="",stafflines=staff_tb[st].stafflines,l=stafflines.length,il=6*staff_tb[st].staffscale +if(!/[\[|]/.test(stafflines)) +return +w=x2-x1;set_sscale(-1) +if(cache&&cache.st_l==stafflines&&staff_tb[st].staffscale==1&&cache.st_w==(w|0)){xygl(x1,staff_tb[st].y,'stdef'+cfmt.fullsvg) +return} +for(i=0;i'} +y=staff_tb[st].y +if(!cache&&w>get_lwidth()-10&&staff_tb[st].staffscale==1){cache={st_l:stafflines,st_w:w|0} +i='stdef'+cfmt.fullsvg;if(ln.indexOf('\n'+ln+'\n';xygl(x1,y,i) +return} +out_XYAB('\n'+ln+'\n\n',x1,y)} +function draw_bar(s,bot,h){var i,s2,yb,w,bar_type=s.bar_type,st=s.st,p_staff=staff_tb[st],x=s.x +if(st!=0&&s.ts_prev&&s.ts_prev.type!=C.BAR) +h=p_staff.topbar*p_staff.staffscale;s.ymx=s.ymn+h;set_sscale(-1) +anno_start(s) +if(s.color) +set_color(s.color);yb=p_staff.y+12;if(p_staff.stafflines!='|||||') +yb+=(p_staff.topbar+p_staff.botbar)/2-12 +if(s.bar_mrep){set_sscale(st) +if(s.bar_mrep==1){for(s2=s.prev;s2.type!=C.REST;s2=s2.prev);xygl(s2.x,yb,"mrep")}else{xygl(x,yb,"mrep2") +if(s.v==cur_sy.top_voice) +nrep_out(x,yb+p_staff.topbar,s.bar_mrep)} +set_sscale(-1)} +if(bar_type=='||:') +bar_type='[|:' +for(i=bar_type.length;--i>=0;){switch(bar_type[i]){case"|":if(s.bar_dotted){w=(5*p_staff.staffscale).toFixed(1);out_XYAB('\n',x,bot,w,h)}else if(s.color){out_XYAB('\n',x,bot,h)}else{sb+='M'+sx(x).toFixed(1) ++' '+self.sy(bot).toFixed(1) ++'v-'+h.toFixed(1)} +break +default:x-=3;if(s.color) +out_XYAB('\n',x+1.5,bot,h) +else +thb+='M'+sx(x+1.5).toFixed(1) ++' '+self.sy(bot).toFixed(1) ++'v-'+h.toFixed(1) +break +case":":x-=2;set_sscale(st);xygl(x+1,yb-12,"rdots") +set_sscale(-1) +break} +x-=3} +set_color();anno_stop(s)} +function out_bars(){var i,b,bx,l=ba.length +set_font("annotation");bx=gene.curfont.box +if(bx) +gene.curfont.box=0 +for(i=0;i\n' +if(thb) +output+='\n'} +function hl_rest(s){var j,p_st=staff_tb[s.st],i=5-s.nflags,x=s.x,y=s.y +if(i<6) +return +if(i==7&&y==12&&p_st.stafflines.length<=2) +y-=6 +j=y/6 +switch(i){default:switch(p_st.stafflines[j+1]){case'|':case'[':break +default:set_hl(p_st,j+1,x,-7,7) +break} +if(i==9){y-=6 +j--} +break +case 7:y+=6 +j++ +case 6:break} +switch(p_st.stafflines[j]){case'|':case'[':break +default:set_hl(p_st,j,x,-7,7) +break}} +function st1(st,s){var tim=s.time +do{s=s.ts_next}while(s.st!=st) +while(s.prev&&s.prev.time>=tim) +s=s.prev +if(s.bar_type) +return s.x +return s.x-s.wl} +for(st=0;st<=nstaff;st++){stl[st]=cur_sy.st_print[st] +xstaff[st]=!stl[st]?-1:0} +bar_set();draw_lstaff(0) +for(s=tsfirst;s;s=s.ts_next){switch(s.type){case C.STAVES:sy=s.sy +for(st=0;st<=nstaff;st++){x=xstaff[st] +if(x<0){if(sy.st_print[st]){xstaff[st]=st1(st,s) +stl[st]=true} +continue} +if(sy.st_print[st]&&cur_sy.staves[st]&&sy.staves[st].stafflines==cur_sy.staves[st].stafflines) +continue +if(s.ts_prev.bar_type){x2=s.ts_prev.x}else{x2=(s.ts_prev.x+s.x)/2 +xstaff[st]=-1} +draw_staff(st,x,x2) +xstaff[st]=sy.st_print[st]?x2:-1} +cur_sy=sy;bar_set() +continue +case C.BAR:if(s.invis||!s.bar_type||!cur_sy.st_print[s.st]) +break +if(s.second&&(!s.ts_prev||(s.ts_prev.type==C.BAR&&s.ts_prev.st==s.st))) +break +ba.push([s,bar_bot[s.st],bar_height[s.st]]) +break +case C.STBRK:if(cur_sy.voices[s.v]&&cur_sy.voices[s.v].range==0){if(s.xmx>14&&s.next&&s.next.type==C.CLEF){var nv=0 +for(var i=0;i0) +nv++} +for(s2=s.ts_next;s2;s2=s2.ts_next){if(s2.type!=C.STBRK) +break +nv--} +if(nv==0) +draw_lstaff(s.x)}} +st=s.st;x=xstaff[st] +if(x>=0){s2=s.prev +if(!s2) +break +x2=s2.type==C.BAR?s2.x:s.x-s.xmx +if(x>=x2) +break +draw_staff(st,x,x2) +xstaff[st]=s.x} +break +case C.GRACE:for(s2=s.extra;s2;s2=s2.next) +self.draw_hl(s2) +break +case C.NOTE:if(!s.invis) +self.draw_hl(s) +break +case C.REST:if(s.fmr||(s.rep_nb&&s.rep_nb>=0)) +center_rest(s) +if(!s.invis) +hl_rest(s) +break}} +for(st=0;st<=nstaff;st++){x=xstaff[st] +if(x<0||x>=realwidth) +continue +draw_staff(st,x,realwidth)} +draw_all_hl() +out_bars() +draw_vname(indent,stl)} +Abc.prototype.draw_symbols=function(p_voice){var bm={},s,x,y,st;for(s=p_voice.sym;s;s=s.next){if(s.invis){switch(s.type){case C.CLEF:if(s.time>=staff_tb[s.st].clef.time) +staff_tb[s.st].clef=s +continue +case C.KEY:p_voice.ckey=s +default:continue +case C.NOTE:break}} +st=s.st +x=s.x;set_color(s.color) +switch(s.type){case C.NOTE:set_scale(s) +if(s.beam_st&&!s.beam_end){if(self.calculate_beam(bm,s)) +draw_beams(bm)} +if(!s.invis){anno_start(s);draw_note(s,!bm.s2);anno_a.push(s)} +if(s==bm.s2) +bm.s2=null +break +case C.REST:if(!gene.st_print[st]) +break +draw_rest(s);break +case C.BAR:break +case C.CLEF:if(s.time>=staff_tb[st].clef.time) +staff_tb[st].clef=s +if(s.second||!staff_tb[st].topbar||!gene.st_print[st]) +break +set_color();set_sscale(st);anno_start(s);y=staff_tb[st].y +if(s.clef_name) +xygl(x,y+s.y,s.clef_name) +else if(!s.clef_small) +xygl(x,y+s.y,s.clef_type+"clef") +else +xygl(x,y+s.y,"s"+s.clef_type+"clef") +if(s.clef_octave){if(s.clef_octave>0){y+=s.ymx-10 +if(s.clef_small) +y-=1}else{y+=s.ymn+6 +if(s.clef_small) +y+=1} +xygl(x-2,y,(s.clef_octave==7||s.clef_octave==-7)?"oct":"oct2")} +anno_a.push(s) +break +case C.METER:p_voice.meter=s +if(s.second||!staff_tb[s.st].topbar) +break +set_color();set_sscale(s.st);anno_start(s);draw_meter(s);anno_a.push(s) +break +case C.KEY:p_voice.ckey=s +if(s.second||!staff_tb[s.st].topbar) +break +set_color();set_sscale(s.st);anno_start(s);self.draw_keysig(x,s);anno_a.push(s) +break +case C.MREST:draw_mrest(s) +break +case C.GRACE:set_scale(s);draw_gracenotes(s) +break +case C.SPACE:case C.STBRK:break +case C.CUSTOS:set_scale(s);draw_note(s,0) +break +case C.BLOCK:case C.REMARK:case C.STAVES:case C.TEMPO:break +default:error(2,s,"draw_symbols - Cannot draw symbol "+s.type) +break}} +set_scale(p_voice.sym)} +function draw_all_sym(){var p_voice,v,n=voice_tb.length +function draw_sl2(){var i,a,d,dy,dy2,dy2o,dz,n,sl +while(1){sl=gene.a_sl.shift() +if(!sl) +break +i=sl[3].indexOf('d="M')+4 +output+=sl[3].slice(0,i) +a=new Float32Array(sl[3].slice(i).match(/[\d.-]+/g)) +a[1]-=staff_tb[sl[0].st].y +dy2o=sl[0].fmt.sysstaffsep+24 +dy2=staff_tb[sl[1].st].y-staff_tb[sl[0].st].y +switch(sl[2]){case"//":case"\\\\":d=-(sl[1].prev.prev.y+staff_tb[sl[0].st].y ++sl[1].prev.next.y+staff_tb[sl[1].st].y) +-2*(a[1]-posy) +a[5]=d-a[5] +a[7]=d-a[7] +if(a.length>8){d=sl[2][0]=='/'?3:-3 +a[8]=-a[8] +a[10]=-a[3]+d +a[12]=-a[5]+d +a[14]=-a[7]} +break +case"/\\":case"\\/":d=sl[2][0]=='/'?dy2-dy2o-10:dy2+dy2o+10 +a[3]+=d +a[5]+=d +if(a.length>8){a[10]+=d +a[12]+=d} +break +default:d=sl[2][0]=='/'?dy2-dy2o:-dy2-dy2o +a[5]+=d +a[7]+=d +if(a.length>8){a[12]-=d +a[14]-=d} +break} +output+=a[0].toFixed(1)+' '+a[1].toFixed(1) ++'c'+a[2].toFixed(1)+' '+a[3].toFixed(1) ++' '+a[4].toFixed(1)+' '+a[5].toFixed(1) ++' '+a[6].toFixed(1)+' '+a[7].toFixed(1) +if(a.length>8) +output+='v'+a[8].toFixed(1) ++'c'+a[9].toFixed(1) ++' '+a[10].toFixed(1) ++' '+a[11].toFixed(1) ++' '+a[12].toFixed(1) ++' '+a[13].toFixed(1) ++' '+a[14].toFixed(1) +output+='"/>\n'}} +for(v=0;v=22?C.SL_ABOVE:C.SL_BELOW +else if(s.multi) +dir=s.multi>0?C.SL_ABOVE:C.SL_BELOW +else +dir=s.stem<0?C.SL_ABOVE:C.SL_BELOW +if(s.multi){for(i=0;i<=s.nhd;i++){ty=s.notes[i].tie_ty +if(!((ty&0x07)==C.SL_AUTO)) +continue +s.notes[i].tie_ty=(ty&C.SL_DOTTED)|dir} +continue} +if(ntie<=1){for(i=0;i<=s.nhd;i++){ty=s.notes[i].tie_ty +if(ty){if((ty&0x07)==C.SL_AUTO) +s.notes[i].tie_ty=(ty&C.SL_DOTTED)|dir +break}} +continue} +if(!sec){if(ntie&1){ntie=(ntie-1)/2;dir=C.SL_BELOW +for(i=0;i<=s.nhd;i++){ty=s.notes[i].tie_ty +if(!ty) +continue +if(ntie==0){if(s.notes[i].pit>=22) +dir=C.SL_ABOVE} +if((ty&0x07)==C.SL_AUTO) +s.notes[i].tie_ty=(ty&C.SL_DOTTED)|dir +if(ntie--==0) +dir=C.SL_ABOVE} +continue} +ntie/=2;dir=C.SL_BELOW +for(i=0;i<=s.nhd;i++){ty=s.notes[i].tie_ty +if(!ty) +continue +if((ty&0x07)==C.SL_AUTO) +s.notes[i].tie_ty=(ty&C.SL_DOTTED)|dir +if(--ntie==0) +dir=C.SL_ABOVE} +continue} +pit=128 +for(i=0;i<=s.nhd;i++){if(s.notes[i].tie_ty){if(pit<128&&s.notes[i].pit<=pit+1){ntie=i +break} +pit=s.notes[i].pit}} +dir=C.SL_BELOW +for(i=0;i<=s.nhd;i++){ty=s.notes[i].tie_ty +if(!ty) +continue +if(ntie==i) +dir=C.SL_ABOVE +if((ty&0x07)==C.SL_AUTO) +s.notes[i].tie_ty=(ty&C.SL_DOTTED)|dir}}} +function set_tie_room(){var p_voice,s,s2,v,dx,y,dy +for(v=0;v24&&s.notes[s.nhd].tie_ty&&(s.notes[s.nhd].tie_ty&0x07)==C.SL_ABOVE);else +continue +s2=s.next +while(s2&&s2.type!=C.NOTE) +s2=s2.next +if(s2){if(s2.st!=s.st) +continue +dx=s2.x-s.x-10}else{dx=realwidth-s.x-10} +if(dx<100) +dy=9 +else if(dx<300) +dy=12 +else +dy=16 +if(s.notes[s.nhd].pit>24){y=3*(s.notes[s.nhd].pit-18)+dy +if(s.ymxy) +s.ymn=y +if(s2&&s2.ymn>y) +s2.ymn=y;y_set(s.st,false,s.x+5,dx,y)}}}} +var musicfont='url("data:application/octet-stream;base64,\ +AAEAAAAOAIAAAwBgRkZUTZHVOuoAAFgMAAAAHEdERUYAFQAUAABX8AAAABxPUy8yWMFdCgAAAWgA\ +AABWY21hcHlUuFMAAAQIAAAD5mN2dCAAIgKIAAAH8AAAAARnYXNw//8AAwAAV+gAAAAIZ2x5ZqUq\ +QgUAAAkcAABF/GhlYWQVmDJzAAAA7AAAADZoaGVhCWn/GwAAASQAAAAkaG10eNm9+0EAAAHAAAAC\ +SGxvY2HHqraAAAAH9AAAASZtYXhwANkBEgAAAUgAAAAgbmFtZeq7sB0AAE8YAAADFXBvc3SIfxKM\ +AABSMAAABbUAAQAAAAEAANGbj/xfDzz1AAsEAAAAAADRlyIXAAAAAOBezej/OPzvBUsEiAAAAAgA\ +AgAAAAAAAAABAAAEiPzvAFwEJf84/XQFSwABAAAAAAAAAAAAAAAAAAAAkgABAAAAkgDhAAUAAAAA\ +AAIAAAABAAEAAABAAC4AAAAAAAEBgAGQAAUACAKZAswAAACPApkCzAAAAesAMwEJAAACAAUDAAAA\ +AAAAAAAAARAAAAAAAAAAAAAAAFBmRWQAQAAA7LcDOP84AFwEiAMRAAAAAQAAAAAAAAF2ACIAAAAA\ +AVUAAAGQAAACWAAAAFcAAAAjAAAAJQAAACT//wBkAAAEIwAABCUAAAHg/9wDugAAAwsAAALSAAAC\ +v/+6AdYAAAMLAAADDgAAAyf/yADIAAABaAAAAa4AAAEiAAABkAAAAXwAAAGQAAABkAAAAYEAAAGQ\ +AAABkAAAAYEAAAGZAAkBmAAJAfQAAAEEABQBBAAKAmsAJAISAAABwgAAAUIAAAFAAAABSv/+ASwA\ +AAIwAAABSgAAAUoAAABkAAABQAAAAUAAAAFAAAABQAAAAGQAAAE2AAAA5gAAATYAAAE7AAABOwAA\ +ATsAAAE7AAABOwAAATsAAAE7AAABOwAAATsAAAE7AAABDQAAAMgAAAD/AAABCwAUAW4AAACMAAAA\ +jAAAAQ0AMgFu//UAqQAAAToAAAFA//0AUAAAAVQAAABkAAABGAAAAlgAAAC2AAABkAAFAIIAAACC\ +AAABLAAAASwAAADuAAAA/wAAAUkAAAGPAAAB2AAAAdgAAAIz//ADIP/hAXv/tAG4/9sBFv9+ARP/\ +2wDcAAAA6P/kAr//tAIz/7QCv/+0Ayv/2wFf/9sCaf9+AV//fgJp/34BXwAAAf0ABQG1AAABtQAA\ +AkQADQJEAA0BGAAAATYAAAEs//8BLAAAAPoAAADIAAABGP84APoAAADIAAAEDQAAAhwADAH0AAAB\ +9AAAAfQAAAH0AAAB9AAAAfQAAAB4AAAALQAAAhwAAAD6AAAA+v/oAcIAAAFIAAABQAAAAgoAAAIK\ +AAAAZAAAAAAAAwAAAAMAAAAcAAEAAAAAAuAAAwABAAAAHAAEAsQAAABgAEAABQAgAAAAIOAA4DDg\ +OeBI4FDgXOBi4GngjOCV4KTgqeCz4QHhu+Hn4gDiSeJk4mvig+Ss5MDk0eTq5QHlMeU55W3lguXQ\ +5eLmGOYk5jDmUOZV6RjpIOkl6V3qAuqk7Knst///AAAAAAAg4ADgMOA44EPgUOBc4GLgaeB64JTg\ +oOCp4LPhAeG54efh8uJA4mDiauKA5KDkwOTO5OHlAOUg5TnlZuWC5dDl4uYQ5iTmMOZQ5lXpEOkg\ +6SXpXeoC6qTsouy3//8AA//kIAUf1gAAAAAfvh+zH64fqAAAAAAAAB+CH3kfLAAAHkkAAAAAAAAA\ +AAAAAAAbkwAAAAAAAAAAGzcAABr0GqcalgAAGlkaThovGisAABdnF2MXLBaIFecAABPaAAEAAAAA\ +AAAAAABYAFoAAAAAAAAAAABcAIAAggAAAAAAAACEAAAAhgCiALQAvAC+AMQAAADaAOAA8gD0AAAB\ +FAAAAAAAAAEcAAAAAAAAAAABJAAAAAAAAAAAAAABKgAAAAAABwAIAAkAAAAKAAsADAANABIAEwAU\ +ABUAFgAAABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgAuAAAALwAxAAAA\ +MgAAAAAAMwAAADQAAAAAADUAAAA2ADcAOAA5ADoAOwA8AD0APgA/AEAAQQBCAEMARABFAEYARwBI\ +AEkASgBLAEwATQBOAAAATwAAAFAAAAAAAAAAUQAAAAAAAABSAFQAAAAAAFUAVgBXAFgAWQBaAFsA\ +XABdAF4AXwBgAGEAYgBjAGQAZQBmAGcAAAAAAAAAaABpAGoAawBsAAAAbQBuAG8AcQByAAAAcwAA\ +AAAAdAB1AHkAAAB6AAAAewAAAAAAAAB8AIEAggCDAAAAhACFAAAAAACGAIwAjQAAAI4AAACPAAAA\ +kAAAAQYAAAMAAAAAAAAAAQIAAAACAAAAAAAAAAAAAAAAAAAAAQAABAAAAAAAAAAAAAAAAAAAAAAA\ +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiAogAAAAqACoAKgA2AD4AbgB6\ +AIYAkgCwASQBgAHwAjQCwANSA7IDxgRkBPYFTgWOBhAGMgZGBpgG6AcIB04HjgfICBQIVAiaCPQJ\ +CAkuCVQJhgmiCcQJ7AoCChwKUgpgCmwKeAqKCqYKyArWCuoK/AsMC1gLaAuCC5wL1AwMDFoMpg0M\ +DW4N7A5kDooOpA7UDv4PVg9wD4oPsBAKECoQaBCQEKQQsBC+EM4Q9hEeEVYRYhFuEXoRhhGoEc4S\ +BBJSErITJhNME3oTzhRCFJYU1hUaFXAWihdiF/gYqhlsGf4a0BvkHNAdEh1WHaAduh3eHfIeBh42\ +HkYeWB50Hooerh7eH7ogdiCeILwg7iEsIV4hoCGyIcAh3CH2IhwiPiJsIoYisCLsIv4AAAACACIA\ +AAEyAqoAAwAHAC6xAQAvPLIHBADtMrEGBdw8sgMCAO0yALEDAC88sgUEAO0ysgcGAfw8sgECAO0y\ +MxEhESczESMiARDuzMwCqv1WIgJmAAABAAAAAAGRAZAAAwAAMREhEQGRAZD+cAABAAAAAAAAAAAA\ +AAAAMQAAAQAAAAAAVwQDACAAABE1NjU0JyY1NDcGFRQXFhUUBxYVFAcGFRQXJjU0NzY1NDUjElc/\ +FSVNTSUVP1cSIwIDAhhDNmA5NGY6MksiOWFNYRgYZkxgOSVKMjpmNDlgNkgAAQAAAAAAIwPoAAMA\ +ABEzESMjIwPo/BgAAQAAAfQAJQPoAAMAABEzESMlJQPo/gwAAf//A2QAJARgAAMAABEzByMkASQE\ +YPwAAgAAAUAAZAKeAAcADwAAEiImNDYyFhQCIiY0NjIWFEcqHR0qHR0qHR0qHQI6HSodHSr+6R0q\ +HR0qAAAABQAAAAAEJAGuAC8ANwA/AEcAUwAAITUzHgEzMjY1NCcuBDU0NjMyFhc3MxcjLgEjIgYV\ +FB4DFx4BFRQGIyInByAiJjQ2MhYUBCImNDYyFhQBETMyNjQmIwM1MxEjNTMyFhUUIwJOHhVPMik7\ +lBkaKhYRWT0kJxkeHgceD0owHzkQIhkyCE5NW09FLiMBmyodHSod/eoqHR0qHf6JKDxGRjzcRkbc\ +cYn6oDxLICEtKAcIFBQjFUNNCw4ZmzpIKBsPFw8JCwIVNzM6TiAgHSodHSodHSodHSoBaf6YYaZh\ +/noeAWgeZ2vSAAUAAAAABCQBrgAaACIAKgAyAD4AACEiJjU0NjMyFhc3MxcHJiMiBhQWMzI2NxcO\ +ATIiJjQ2MhYUBCImNDYyFhQBETMyNjQmIwM1MxEjNTMyFhUUIwMCZ3WCWiUpGx4eCCEkXjg2Njg2\ +TREjFFmxKh0dKh396iodHSod/okoPEZGPNxGRtxxifprZ2V3DBIepgSMbZhtST4KSlEdKh0dKh0d\ +Kh0dKgFp/phhpmH+eh4BaB5na9IAAAAD/9wAAgHeArMABwAPAE0AAAAiJjQ2MhYUBCImNDYyFhQX\ +NDYzMhYVFAcWMzI2NTQvAQMnEy4BNTQ+ATc2MzIWFRQGIyImNTQ3JiMiBhUUHwETFwMeARUUDgEH\ +BiMiJgGeIBgYIBj+fiAYGCAYWxsUEx4sFykmNiZ7zyvRWkgcExQmMzA4GxQTHiwXKSY2JnrUK9Va\ +SBwTFCYzMDgBUhggGBggVBggGBgg0xIcGhEdDhctJi0mZf7eIAElR3E4Fi0RERM4IRIcGhEdDhct\ +Ji0mZQEmH/7XR3I4Fi0RERM4AAUAAP9WA7wDYgAXABsAHwAjACcAAAEzFR4BFzMVIw4BBxUjNS4B\ +JyM1Mz4BNxEjFhcTETY3JzMmJwMRBgcBxDRwnwmsrAmfcDRwnwmsrAmecZQHjTSOBpSUBo40jgYD\ +YqsLtoA0f7cLq6sLt380grQL/ov0FAEI/vgU9DT0FP74AQoU9gAAAAAEAAD9bwKnBIgACwBEAE4A\ +YwAAAQYVFBc+ATU0Jw4BExcVFAYjIiY1NDYzMhYUBgcWMzI2PQEnBiMiJjU0Nz4GPwEmNTQ2NxYV\ +FAYHFzYzMhYVFCc0JiMiBiMTPgEnDgEVFBYXLgE1NDcnDgEHHgEzMjcBbAcFSHU2OUJLF09NUl9A\ +Mi9BPy8tGC9AFxwNmNs6DSQgLh0xEhcWDWxJYlx5FBQLcYhOYlACBwIhW0HiNkYnHT0/phGPbAEC\ +oXkKFgNTNy0jWjGVSHINB177bOwDTFxTQS1IO1g3ARk8RgPpAsedhWYXMCUtGicOERCWZoyhBzrl\ +iKlfzwKcc866PmYB/p0SXfILRjEgQhIORUepLr1yl2eNowIAAgAA/fwC0gIAAGQAaAAAATI2NTQn\ +JiMiBw4CByYnJicRIxEzETY3NjceAxcWMzI2NTQnJiMiBxYXFBYVFAYrASY1NDc2NzYzMhcWFxUU\ +BgcGIyInBxc2MzIWFxYdAQYHBiMiJjU0NzMyFhUUBhUGBxYBMxEjAd4+Sg0aSUY8AgYKBCIaHi4c\ +HC4eGiIGFAwZDyclMT0SJFIvMTINAjMhBUQFGlUnI15VNwhaSB8tND8iIj80QmYeKAg3Vl1MckQF\ +ITMCDzA1/k17e/4geEotLWxJBQ4bCmInKx/+BAQA/hEfKydiCyoXHQkbe0IxNmIaECgDDQQeKxky\ +FAtEGQ1PNFMSTm8cDRdLShc+LDlDElM0UE48MhkrHgQOBCYQHAPe/AAAAAP/uv2lAsMA/wAqADYA\ +QgAANzQ2MzIWFxYVFAYHDgEHNjc+ATc2NTQmJy4BIyIGBz4BMzIWFRQHBiMiJgUiJjU0NjMyFhUU\ +BiciJjU0NjMyFhUUBhOLZ1VrKy9GVWbPleWFMTQTChEdHDQzO2EWGCMbLj0nITEzRQKCFh0aFBUe\ +GhoXGxwVFBwbHWKANDk+cn+6T2BNCD2FMWNQK2ZHUCMiF09GHhdBLzIgHlGKHBcWHB0VFh3yHhkV\ +GhsUGR4AAAIAAP8GAXIA+gADAAcAADczEyMDMxMj3JQCltyUApb6/gwB9P4MAAAEAAD+CgIfA6oA\ +CQAgAGMAbgAAJRYXPgE1NCYjIgMCJw4BFRQXLgE1NDY3JicOAQceATMyFx4BHwEwHQEUIyImNTQ2\ +MzIWFRQGBxYzMjY1NC8BBiMiJjU0Nz4BNz4CNyY1NDY3HgEVFAYHHgEXNjMyFxYVFAcGAwYVFBc+\ +ATU0JwYBSRMGTUdWQg4NGAEsOR8gKkw7BQlxVQEFbYIDIgMGAgJ6NlMzKCU1MScVIiMsAQ0JFY2Z\ +Lg5MHgUjJxIOWUAvGUlhAggEEghcOTJjNmQDBjdeKV91xFsSTzM2Vv7gAQsQCTQnKSYSQSk4ThA+\ +Wlp5U3F+GiBAFxcbB39IMSQzNyQiKAEMMzUPCY0BkopqURxQGAQgIQ3CB25/EzNiW22HTBFuJAJD\ +NmJ3MBsDVh4iOSQlezNGJiYAAAAAAgAA/mMCQgGaAGMAZwAAATI2NTQnJiMiBw4BByYnJicRIxEz\ +ETY3NjceAxcWMzI2NTQnJiMiBxYXFBYVFAYrASY1NDc2NzYzMhcWFxUUBgcGIyInBxc2MzIWFxYd\ +AQYHBiMiJjU0NzMyFhUUBhUGBxYBMxEjAX4yOwoVOjgwAgwEHRMYJRYWJRgTHQQRChMMIB0nMQ4c\ +QyUnKAoCKhoENgQVRCMYSUYtBkg6GCUrMRsbNCg1UhggBi1GST1bNgQaKgIMJiv+o2Ji/oBgOyQk\ +VzsFHQtSHCIZ/moDM/50GSIcUggiEhcIFmI1KyhOFQ0gAgsDGCIUKBAJNRULQCtBDj5aFgoSPDsT\ +MiMuNQ9BK0A/MCgUIxgDCwMfDRYDGPzNAAAAAAP/yP4eAjYAzAAmAC8AOwAANzQ2MzIXFhUUBw4B\ +BzY3Njc2NTQnLgEjIgYHPgEzMhYVFAcGIyImBSImNDYyFhQGJyImNTQ2MzIWFRQGD3BSgjwjeEPG\ +acBeRx4JLRgtIDBSChIWFiU5IBklLDkCAhIXFCIYFRUSFhUSERYVIU1eVzNaxmw7VwYzZEqMLylm\ +MxoUQjYXDTwmKBoYU3UXJBYXIhjCGBQRFBUQExkAAAAAAwAAAAAAyADwAAkAFQArAAA3BhUUFjMy\ +NjU0JyIGFRQeARc2NTQmByImNTQ2Ny4BNTQ2MzIWFRQHFhUUBlYsHhARFAIOEwoJECUUNCU1IyIR\ +DCgeKTE3IzR0EB4UHhsRF4URDgoQCAwLHQ4X3CgeFxoLDxMQGiIfHSUOHB8gJgACAAD//wFnAQMA\ +MABiAAAXIjU0MzIVFAYVFDMyNjU0IyIHBiMiNTQ3NjM2MzIWFRQHBgcOAQcGFjc2MzIWFRQGJyI0\ +OwEyNzI0MzY3PgE1NiMiBwYnJjc+ATc2OwIyFxYzMjc2BwYHBhUUMzIVFCsC1D4YGxIdFyweChQW\ +CAsmBgkjSQ8FFhpICgYBAgUIDA4gI0P6CQkJFAUBAQ0XAQIIDgkIDA0KDwYRAjIEAgMGDgwIAggX\ +BkcKAhUMDCcnATchGwcWBg5CIxwMDQ8WVAsBAgUQBwkQAgYLCgUCAyQgKDkBEgwEHz0BBQIVCQ4G\ +BhIHGAJABwYCCBKtHQUFCAoIAAAAAAIAAP8GAa4A+gALABQAADMUFjMyNjU0JiMiBgc0NjIWFAYi\ +JooqIyIrJyYlKIp9tH19tH1ieHlhZXV2YWeQkdKRkgAAAQAA/wYBIgD6AAkAADE3MxEXFSM1NxFk\ +fUHwQfr+Ph4UFB4BLAAAAAEAAP8GAY8A+gA8AAA3MhUUBw4DBzYzMhYzMjc+AjMOAgcGBwYjIiYj\ +IgYjIjU0Jz4FNTQnIgcyFhUUBiMiNTQ+AcfIBQ02QG82EyAbZBwYHgUQDAEBBQUBBxAaKRp0FR9W\ +AgcBAiw+RzwoU04aHCk3Hkw8WPp+Gg4hLh1ELQwjDgMNCwUWFgMpDhgnJhABAiFFODwyOBhiATUl\ +Hh8pZyg5GQAAAQAA/wYBdQD6ADkAADcyFhUUBiMiJjU0NzYzMhcWFRQGBx4BFRQHBiMiJicmNDYz\ +MhYVFAYjFjMyNjU0JicmNDc+ATQmIyJmGyInIRsyHzNZRiZERj0+UUskTSdXGCMyICIqJRsMPyQr\ +SC4WFi9LKSQ8qhwXGyMrIy8aKhMiSC5ECwtFLUMnExYUHUwuIRsZHikxJyY6CAQiBAk3UDAAAAEA\ +AP8GAZAA+gARAAAFFyM3NSM1NjUzATM/AREzFSMBRTLIMuGTo/77sAFjS0vRKSkxKPKA/o6Wkf7Z\ +KAAAAAABAAD/BwF+APoALwAAFzYzMhYVFAYjFjMyNz4BNTQnJiMiBxMhDgErAQc2MzIXHgEVFAcO\ +AiMiJy4BNTQSHCEbKiAcGiQxHBMJHhwoTkgKAWILNSXVBjlCUzEhK0MWQCslPCsQHl8gIBccICEe\ +FB8gORwaNQEiJDp5Hh8VQSVPLxAQAhQJMhIjAAAAAAIAAP8GAYEA+gAJACwAABcyNjU0JiMiBxYT\ +FhUUBiMiJjU0NjMmIyIGFT4CMzIWFRQGIyImJz4BMzIWyCktKigsMAfdGyMYHiIbEBY3NS8VGC0e\ +TE9xSGFmAQFsWzA/0kUsIjAlngGfGiYZKB4bDB4jeF8LCglANkRZgnhpkRIAAQAA/wYBkAD7ACgA\ +ADciDgMHNz4IMzIWMzI2Nw4EFSM2NzY3NjcGIyImYRQaFAkRBQoBDAILBQsJDRAJL3UjGjsRG0Ua\ +HgiCAQgRaB4tER4lYKEFDgkbBnQBCwIJAQYBAwEmFw5Dp0FcQitFGzaHJzkKKAADAAD/BgGEAPoA\ +DgAcADQAABcOARUUFjMyNjU0LgM3PgE1NCYiBhUUHgMHLgE1NDY3MhYVFAYHHgEVFAYjIiY1NDaa\ +NixYLCo/DyAdMT4zI0RSMwocEjFwMSlmSktlKjA6MnVNTHY5KhkkGx0wKR8OFxINE1oaIhwdMCgg\ +DxcTChU/GDw1M00BRjInNBcaOjU3SkgwJDUAAAIAAP8GAYEA+gAJACwAADciBhUUFjMyNyYDJjU0\ +NjMyFhUUBiMWMzI2NQ4CIyImNTQ2MzIWFw4BIyImuSktKigsMAfdGyMYHiIbEBY3NS8VGC0eTE9x\ +SGFmAQFsWzA/0kUsIjAlnv5hGiYZKB4bDB4jeF8LCglANkRZgnhpkRIAAQAJ/woBmQD5ADAAACUw\ +FzY1NCYjDgEVFBcWMzI3NjcUHgEVDgEHIicmJzQmNTQ3MhYXFhUUBiMiJjU+ATMBLxIEPB8zQCch\ +MCsoHCoJCBtVVk87OwQB2yRAESIkHCApAiAapAMFCBQeAmJqjjMqIhhYAQQDAVVQATk5ZgIrAuYC\ +HhQnJCU5LhwWJgACAAn+ogGZAV4AOAA/AAAlMBc2NTQmIyIHERYzMjc2NxQeARUOAQcjFSM1Jicm\ +JzQmNTQ3NTMVMjYzMhYXFhUUBiMiJjU+ATMDEQYVFBcWAS8SBDwfAxAMDCsoHCoJCBtRVQEjPS47\ +BAGrIwMHAyRAESIkHCApAiAaej0nCqQDBQgUIgT+SwQiGFgBBAMBVFEBaGsJLTlmAisCyxlpZgEe\ +FCckJTkuHBYm/pcBli6RjjMNAAABAAD/BgH0APoACwAANTM1MxUzFSMVIzUj10bX10bXI9fXRtfX\ +AAAAAQAU/gYA5AIAABMAABMWBwYnJgI1NBI3NhcWBwYCFRQS3AcNCQVJa2tJCQsHBzxGR/4WCAUD\ +BlcBIH18ASJWCwcFCUn+54iG/uUAAAEACv4CANwB/QATAAATNhI1NAInJjc2FxYSFRQCBwYnJhQ7\ +R0Y8CQwKB0lra0kICwb+FkkBG4aIARlJCwQECVb+3nx9/uBXCQkEAAAEACT/VgJMAKoACwAPABMA\ +HgAABTQmIyIGFRQWMzI2NzMRIwEzESMkFAYjIiY1NDYzMgGoVDwgNFc9IS9yMjL+CjIyAfV7ZmV8\ +eWhmIDVOJx81SyPq/qwBVP6s5nhGSTk/QwAAAgAA/yQCEgDcAAMADwAANxUhNSUzFSE1MxEjNSEV\ +Ix4B1v4MHgHWHh7+Kh5BgoKbNzf+SDc3AAACAAD/fwHCAIEACwATAAAFNCYjIgYVFBYzMj4BFAYi\ +JjQ2MgFRWjYgNF03IS9xfch9fcgeNE8nHzRMI3RsS0tsSwAAAAIAAP92AUIAigAMABgAACU0JiMi\ +BhUUHgEzMjYnMhYVFAYjIiY1NDYBIxocO5MEGxc7k2FAQHtHQEB7Lg4ZWygEDhVbhEwpP2BMKT9g\ +AAAAAQAA/3kBQACHAAsAACUUBiMiJjU0NjMyFgFAeVkyPHpYMjwoRmk4J0VqOAAB//7/bwFMAJEA\ +CwAAJzcXNxcHFwcnByc3AhuMjBuGhhyLixyGcSB2dSBwcCF1dSFwAAAABQAA/2oBLACWAAUACwAR\ +ABcAHwAAFwcWMzI3LwEGFRQXPwEmIyIHHwE2NTQnBjQ2MhYUBiKWRx0qKR9dRx0dXEgfKSodXEcd\ +HfJYfFhYfBJIHR1aSR8pKh1YSB0dWkgfKSodhXxYWHxYAAAAAQAA/wYCMAD6AAMAABUBMwEBuHj+\ +R/oB9P4MAAEAAP90AUoAjAADAAAxNxcHpaWljIyMAAEAAP90AUoAjAACAAAVGwGlpYwBGP7oAAEA\ +AP/OAGQAMgAHAAAWIiY0NjIWFEcqHR0qHTIdKh0dKgAAAAEAAP95AUACqAAPAAARIREUBiMiJjU0\ +NjMyFxEhAUB5WTI8elgxH/7eAqj9gEZpOCdFahwBxQAAAQAA/3kBQAKoABMAABEhERQGIyImNTQ2\ +MzIXESE1ITUhAUB5WTI8elgxH/7eASL+3gKo/YBGaTgnRWocARF4PAAAAAABAAACMAFAAqgAAwAA\ +ESEVIQFA/sACqHgAAAAAAgAAAXwBQAKoAAMABwAAESEVIREhFSEBQP7AAUD+wAH0eAEseAAAAAEA\ +AP/aAGQAPgAHAAAWIiY0NjIWFEcqHR0qHSYdKh0dKgAAAAEAAAMCATYDwAAFAAARIRUhFSMBNv7o\ +HgPAHqAAAAAAAQAAAyoA5gQ4ADkAABMiJjU0NjMyFhUUDwEUMzI2NTQmKwEiNTQ7ATI2NTQmIyIV\ +FBYVFCMiJjU0NjMyFhUUDgEVFBYVFAZQJykQDg8SCwwhFSQRDxIUFCgXJQ4OFQIhDBIlKyMtIyMU\ +MwMqHhgOFhAMEgcHDSofDxkOECUVDRMNAwgIHBEMFR4iGhgjEwIBIg8jLQAAAAABAAADAgE2A8AA\ +BQAAARUjNSE1ATYe/ugDwL6gHgAAAAEAAPzvATsAAAAPAAAVNTMeBBUUBzY1NCYnHgY/UVA3LhKR\ +cO/vNXBlbIlJYGlBSY/fKgABAAAAAAE7AxEADwAAMTUzPgE1NCcWFRQOAwcecJITLjdQUT8G7x/h\ +k0dIZ15Iim1ncTUAAgAA/UQBPAAAABgAJgAAGQEzHgYVFAcWFRQHNjU0LgMjNR4DFzQ2NTQuAx4G\ +JzM7OC4cEhMeBSk+SEAWCURNVhUBKT5IQf6pAVcbOTM3Oj1IJSwrKSs2ORkhN2RFNBirJFRDXSkE\ +DAQ3ZUUzGQAAAAIAAP//ATwCvAAYACYAABURMzI+AzU0JxYVFAcWFRQOBQc1Mj4DNTQmNQ4DHhZA\ +SD4pBR4TEhwuODszJwYWQUg+KQEVVk1EAQFXGDRFZDchGTk2KykrLCVIPTo3MzkbqxkzRWU3BAwD\ +KV1DUwAAAAADAAD9KgE8AJEAGwApADcAABkBMx4GFRQHFhUUBxYVFAc2NTQuAiM1HgMXNDY1NC4C\ +IyceAxc0NjU0LgIjHgYnMzs4LhwSEhITHgU9V1UcCURNVhUBPFZVHQIJRE1WFQE8VlUd/pACARs5\ +Mzc6PUglLCsnLSwrKSs2ORkhRHdIKakkVEFdKQQNA0R2SCmrJFNCXSkEDQNEdkgpAAADAAD/VgE8\ +Ar0AHAApADYAABEzMj4DNTQnFhUUBxYVFAcWFRQOBQcjNzI+AjU0JjUOAycyPgI1NCY1DgMeFkBI\ +PikFHhMSEhIcLjg7MycGHh4dVVc9ARVWTUQJHVVXPQEVVk1EAVcYNEVkNyEZOTYrKSssLScrLCVI\ +PTo3MzkbqilJeEQEDAQpXUNUhilJeEQDDQQpXUNUAAQAAP1CATwBVAAgAC4APABKAAAZATMeBhUU\ +BxYVFAcWFRQHFhUUBzY1NC4DIzUeAxc0NjU0LgMnHgMXNDY1NC4DJx4DFzQ2NTQuAx4GJzM7OC4c\ +EhISEhITHgUpPkhAFglETVYVASk+SEEWCURNVhUBKT5IQRYJRE1WFQEpPkhB/qgCrBs5Mzc6PUgl\ +LCsnLSwrJy0sKykrNjkZITdkRTQYqyRUQ10pBAwEN2VFMxmrJFRDXSkDDgM3ZUUzGaskVENdKQQM\ +BDdlRTMZAAQAAP6OATwCoAAfACwAOQBGAAAZATMyPgI1NCcWFRQHFhUUBxYVFAcWFRQOBQc1Mj4C\ +NTQmNQ4DJzI+AjU0JjUOAycyPgI1NCY1DgMeHFVXPQUeExISEhISHC44OzMnBh1VVz0BFVZNRAkd\ +VVc9ARVWTUQJHVVXPQEVVk1E/o4CrClId0QhGTk2KykrLC0nKywtJyssJUg9OjczORuqKUl4RAMN\ +BCldQ1SGKUl4RAMNBCldQ1SGKUl4RAMNBCldQ1QAAAAFAAD9VQE8AhIAJAAyAEAATgBcAAAZATMe\ +BhUUBxYVFAcWFRQHFhUUBxYVFAc2NTQuAyM1HgMXNDY1NC4DJx4DFzQ2NTQuAyceAxc0NjU0LgMn\ +HgMXNDY1NC4DHgYnMzs4LhwSEhISEhISEx4FKT5IQBYJRE1WFQEpPkhBFglETVYVASk+SEEWCURN\ +VhUBKT5IQRYJRE1WFQEpPkhB/rsDVxs5Mzc6PUglLCsnLSwrJy0sKyctLCspKzY5GSE3ZEU0GKsk\ +VENdKQMNBDdlRTMZqyRUQ10pBAwEN2VFMxmrJFRDXSkDDgM3ZUUzGaskVENdKQQMBDdlRTMZAAAF\ +AAD9vAE8AnkAIwAwAD0ASgBXAAAZATMyPgI1NCcWFRQHFhUUBxYVFAcWFRQHFhUUDgUHNTI+AjU0\ +JjUOAycyPgI1NCY1DgMnMj4CNTQmNQ4DJzI+AjU0JjUOAx4cVVc9BR4TEhISEhISEhwuODszJwYd\ +VVc9ARVWTUQJHVVXPQEVVk1ECR1VVz0BFVZNRAkdVVc9ARVWTUT9vANXKUh3RCEZOTYrKSssLScr\ +LC0nKywtJyssJUg9OjczORuqKUl4RAMNBCldQ1SGKUl4RAMNBCldQ1SGKUl4RAMNBCldQ1SGKUl4\ +RAMNBCldQ1QAAAACAAD/ZADhAbAACgAWAAA3IgYdATY3NjU0JjcyFhUUBwYjETMRNmcUKyImKx0H\ +IzlLUkQoI3omE7kPODsvGyYmMiNJTFICTP68NAAAAgAA/oYAxQF6AAMADAAAFzc1BxEVNxEjNQcR\ +NxyQkKkZrAFlLZYtAUnoNP3A4jMCQwEAAAIAAP6YAP8BaAADAB8AADcVNzUDIzUHNTc1BzU3NTMV\ +NzUzFTcVBxU3FQcVIzUHU1paHjU1NTUeWh01NTU1HVpGpxun/jejD1wPpw9aD6ifHKujD1wPpw9a\ +D6ifHAAAAAEAFP+EAQsAegAeAAAXNSYnBzAVIzUzNycwIzUzFRYXNzA1MxUjBgcXMDMVwygMM0g5\ +MzM5SCESNEg5IRM0OXw7Jg0zO0oyMkg5IhEzOUciETRIAAQAAP9qAWwBsAAOABwAKwA6AAA3DgEd\ +ATI3Njc2NTQnJiM3MhYVFAcGBwYjETMRNhcOAR0BMjc2NzY1NCcmIzcyFhUUBwYHDgEjETMRNk4R\ +Hg4eHwwEChARGR0rCRgrNS8fGdERHRAeHQsGCxAPFh8qCxkoFjcWHht9AR4QxikrNA0ZHhQVJjkh\ +EiA5NEACRv7BMiYBHRHGKS8wExMcFhUmNiQWHD4vGyUCRv7BMgAAAQAA/wYAjAD6AA4AADcVBhUU\ +FxUuAzQ+AoxQUBolMhsbMiX6FEOjpkAUDx86WnBaOh8AAAEAAP8GAIwA+gAOAAA1HgMUDgIHNTY1\ +NCcaJTIbGzIlGlBQ+g8fOlpwWjofDxRApqNDAAACADL/ZAENAbAACgAWAAA3IgYVFBcWFzU0Jicy\ +FxEzESInJjU0NqsXIC4uGiozOiMjOlJPOXomGzBFQgPLEh4mNAFE/bRSUEUjMgAABP/1/2oBbAGw\ +AA4AHQAsADsAADcjIgcGFRQXFhcWMzU0JicyFxEzESImJyYnJjU0NhcOAR0BMjc2NzY1NCcmIzcy\ +FhUUBwYHDgEjETMRNlcBERAKBAwfHg4eKzAZHxY3FysYCSvpER0QHh0LBgsQDxYfKgsZKBY3Fh4b\ +fRUUHhkNNCspxhAeJzIBP/26JRs0OSASITkmAR0RxikvMBMTHBYVJjYkFhw+LxslAkb+wTIAAAAA\ +AQAA/sAAqQFAABMAABMzFTcVBxU3FQcVIzUHNTc1BzU3RB5HR0dHHkREREQBQKIOXA5/DloPqKIO\ +XA5/DloPAAMAAP6YAToBaAAjACcAKwAANzUzFTcVBxU3FQcVIzUHFSM1DwEjNQc1NzUHNTc1MxU3\ +NTMVAzUHFTcVNzXpHjMzMzMePR48AR4zMzMzHj0eHj1bPcCong9cD58PWg+1qhKspBCong9cD58P\ +Wg+1qhKspP79nhGfuJ4RnwAB//0AAAE/APQAGAAANwYjIiY1ND8BNi8BJjU0NjMyMRcFFhUUBxIC\ +AwcJBs8ODs0ICwcBAgEfDg4BARAICgNJBwZPAwsKEgFrBg4NBQAAAAEAAAAAAFAAUAAJAAA1NDYy\ +FhQGIyImFyIXFxEQGCgRFxciFxgAAAABAAAAAAFUADIAAwAAMTUhFQFUMjIAAAABAAAAAABkARgA\ +AwAAMwMzAygoZCgBGP7oAAAAAQAAAAABGAE1AAUAADEbASMnB4yMQVhaATX+y8bGAAACAAAAAAJY\ +AUoADgAZAAAxNDYzMh4CFSMuASIGByEiJjQ2MzIWFRQGs3k5a1UzDwui4KILARwXJSUXGSMjmLIs\ +UYBNboaGbiQwJCQYGSMAAAEAAAAAALYBLQAYAAATMhYXFhUUBw4BIzAnJjU0NjU0Iy4BNTQ2Vhsb\ +EBoyGUQQBgFHFBsoLQEtDBEdMD08HS0DAQIIaxMPASYcHjEAAAAAAgAFAAABjgH1ABEAIwAANxM2\ +MzIXFhUUBwMGIyInJjU0JxM2MzIXFhUUBwMGIyInJjU0mM0LCgYDCwXMCQsEBguLzQoLBgMLBcwI\ +DAQGCyMBwBICBQoGDP4/EQMGDAcHAcASAgUKBgz+PxEDBgwHAAEAAP8GAIIA+gADAAA1MxEjgoL6\ +/gwAAAEAAAAAAIIA+gADAAA1MxUjgoL6+gAAAAEAAP+DASwAAAADAAAxIRUhASz+1H0AAAEAAAAA\ +ASwAfQADAAA1IRUhASz+1H19AAEAAP5+AOsBhwATAAATFwcXJiMiBhUUFyY1NDYzMhcnNym9Z2wy\ +NB8mOHg0JSIih2QBh+XZzy4kHTU0S00jLRW8tAAAAQAA/w0BAADAABYAADcOAiMiJjU0NjIWFRQH\ +MjY3NjIXAyerAxkaEys3JjgpFyIzIQIVA5YwPAEHBCkoHyAeGR0bISwCAv5vEAAAAAEAAP4MAUgA\ +wAAkAAAXBiMiJjU0NjMyFhUUBzI/AQYjIiY1NDYzMhYVFAcyNzYyFwMnqyghKzcnGxwpF0ELPDYY\ +KzcnGxwpF0guAhUDxS3EDCgoICAfGR0bIsoMKSgfIB4ZHRtNAgL9bgwAAAEAAP4MAY8BwAA2AAA3\ +BiMiJjU0NjMyFhUUBzI/ASIOASMiJjU0NjMyFhUUBzI3NjIXAScTBiMiJjU0NjMyFhUUBzI39igf\ +KzcnGxwpFz8LOgEgHBMrNycbHCkXSC4BFgP+9C1VKCErNycbHCkXQQs8DCgoICAfGR0bIssJBCko\ +HyAeGR0bTQIC/G4MASQMKCggIB8ZHRsiAAAAAAEAAP0MAdoBwABFAAATBiMiJjU0NjMyFhUUBzI/\ +AQYjIiY1NDYzMhYVFAcyPwEGIyImNTQ2MzIWFRQHMj8BIg4BIyImNTQ2MhYVFAcyNzYyFwEnqygh\ +KzcnGxwpF0ELOighKzcnGxwpF0ELOigfKzcnGxwpFz8LOgEgHBMrNyY4KRdILgIVA/6pLf48DCgo\ +ICAfGR0bIsoMKCggIB8ZHRsiygwoKCAgHxkdGyLLCQQpKB8gHhkdG00CAvtuDAAAAAEAAP0MAhkC\ +rgBWAAAlBiMiJjU0NjMyFhUUBzI/AQYjIiY1NDYzMhYVFAcyPwEiDgEjIiY1NDYzMhYVFAcyNzYy\ +FwEnEwYjIiY1NDYzMhYVFAcyPwEGIyImNTQ2MzIWFRQHMjcBPyghKzcnGxwpF0ELNigfKzcnGxwp\ +Fz8LNAEgHBMrNycbHCkXSC4BFgP+ai1VKCErNycbHCkXQQs6KCErNycbHCkXQQs0DCgoICAfGR0b\ +IsQMKCggIB8ZHRsixwkEKSgfIB4ZHRtNAgL6gAwBJAwoKCAgHxkdGyLIDCgoICAfGR0bIgAD//D/\ +BgImAPoABwAPABMAADYiJjQ2MhYUACImNDYyFhQFATMBUDIjIzIjAYgyIyMyI/3SAbh+/kdLIzIj\ +IzL+zyMyIyMyWgH0/gwABP/h/wYDBwD6AAcADwATABcAADYiJjQ2MhYUACImNDYyFhQFATMBMwEz\ +AUEyIyMyIwJ3MiMjMiP84wG4e/5HeQG4e/5HSyMyIyMy/s8jMiMjMloB9P4MAfT+DAAC/7T/iAF8\ +ARgAEQA7AAA3FjMyNjc2NTQnJiMiBgcGFRQXIicHMzIUKwEiNDsBEzY1NCMiDgMHBiY3Njc2MzIW\ +Fz4BMzIWFRQGxQIFEjIODQ8CBBI1Cw8nKhktNAsL4QsLS2gGCwgMDwsZCgUbBTEPFyUjJAcdJiMe\ +LVkoATUkICYlBQEzHCckKS4geh4eAR0SDA8HFBIsEAgPCVgQGRMaHg80MElrAAAB/9v/9gG+ARgA\ +UwAANwYHBisBIj8BNiYjIgYHBiY3PgMzMhc2MzIXPgEzMhYVFA8BBhUUMzI3PgU3NhYHDgIjIiY1\ +ND8BNjU0IyIPAQYHJwYmPwE2NTQjIgdQBwgEBDUNDUIEBggNFiQFFQQUECIfEjcLJCQtCQspExkk\ +BS8ECAEEBQsHDAMNAQYVBhIXLR8VGQU0ARUbCEEIDyYNCARDARUbCBURAwEapw0PGjkIDAklGjAS\ +KCgoEBgjGgsPfQsJDgIDCQYPBRIBCQ0LHx8aFhMNDYgCBA4UqxYBAQEPCKsCBA4UAAAAAf9+/2AB\ +XgG4AEEAAAciJjU0NjMyFhUUBwYVFDMyPgc3IyI1NDsBPgEzMhYVFAYjIiY1NDc2NCMiDgcHMzIU\ +KwEOATIgMBcTEhcSChkLEA8LDQoODRQKNRMRQRRpNCAwFxMSFxIKGQcMCgcIBQYDBgE2ExM/IXag\ +JiAaIhQPDgsHDQ4GERMlJDw3VScVE0tfJiAaIhQPDgsGHAULChQNGg0eBijFwQAAAf/bAAABEwET\ +ACoAADc+ASYjIgYPAQYHBisBIj8BNiYjIgYHBiY3PgEzMhYXNjMyFhUUIyImNTTQBQMDBRQkCT8H\ +CAQENQ0NQgQGCA4XIgUVBCE4IhsdBB8kGiArDxvnAwcELBicEQMBGqMNDxo1CAwJPEAaDSceGTcW\ +DRQAAAABAAAAAADcARgAMQAAMyImNTQ2MhYVFAcWMzI2NTQuAicmNTQ2MzIWFRQGIyImNTQ3JiMi\ +BhUUHgIXFhUUUB4yFRoXEAwTFiEJCxgGOjctIjYWEA0WBw8RDxkREh4FMCsbEBYQDBIQEBYSCw8H\ +DQQlMCMoJBgQGBQOAw4TEQ0JEwwRAyArVQAAAf/k//wA5wEPADwAACcGLgE/ASIGIyImDgEHBicm\ +Nz4BNx4BMzI2MzIXFhQPAQYVFDMyNjMyFxY3NiciNTQ2MzIVFAYrAS4BIyIKBgsBBbAFHwwDFAwf\ +BQ8EAwgMCwEOLxUiKwQIBQsHngICAQ0GGiMNCAUHJBINJigeHRglCA4DBAgNBsEGBgIyBQ0LCRIe\ +MAIBBgsBAREHpwYCAwMaCg4NAx0OFTceLwMYAAX/tP+IBUsBGAAQACIAtQDGANcAACUWMzI2NzY0\ +JyYjIgYHBhUUBRYzMjY3NjU0JyYjIgYHBhUUFyInBzMyFCsBIjQ7ARM2NTQjIg4DBwYmNzY3NjMy\ +Fhc+ATMyFz4BMzIWFz4BMzIXPgEzMhYXPgEzMhc+ATMyFhc+ATMyFhUUBiMiJwczMhQrASI0OwET\ +NjU0IyIGBxUUBiMiJwczMhQrASI0OwETNjU0IyIGBxUUBiMiJwczMhQrASI0OwETNjU0IyIGBxUU\ +BiUWMzI2NzY0JyYjIgYHBhUUBRYzMjY3NjQnJiMiBgcGFRQCCgIFEjIODQ8CBBI1Cw/+ywIFEjIO\ +DQ8CBBI1Cw8TFhktNAsL4QsLS2gGCwgMDwsZCgUbBTEPFyUjJAcdJiMvExIiGyMkBx0mIy8TEiIb\ +IyQHHSYjLxMSIhsjJAcdJiMeLWtJFhktNAsL4QsLS2gGCw4UFGtJFhktNAsL4QsLS2gGCw4UFGtJ\ +FhktNAsL4QsLS2gGCw4UFGsDgwIFEjIODQ8CBBI1Cw/+ywIFEjIODQ8CBBI1Cw8oATUkIUoFATMc\ +JyQpBgE1JCAmJQUBMxwnJCkuIHoeHgEdEgwPBxQSLBAIDwlYEBkTGh4PMhwWExoeDzIcFhMaHg8y\ +HBYTGh4PNDBHbSB6Hh4BHRIMDxgjAUdtIHoeHgEdEgwPGCMBR20geh4eAR0SDA8YIwFHbSgBNSQh\ +SgUBMxwnJCkGATUkIUoFATMcJyQpAAT/tP+IBAYBGABvAIAAkQCjAAAhIicHMzIUKwEiNDsBEzY1\ +NCMiBgcVFAYjIicHMzIUKwEiNDsBEzY1NCMiBgcVFAYjIicHMzIUKwEiNDsBEzY1NCMiDgMHBiY3\ +Njc2MzIWFz4BMzIXPgEzMhYXPgEzMhc+ATMyFhc+ATMyFhUUBicWMzI2NzY0JyYjIgYHBhUUBRYz\ +MjY3NjQnJiMiBgcGFRQFFjMyNjc2NTQnJiMiBgcGFRQDUhYZLTQLC+ELC0toBgsOFBRrSRYZLTQL\ +C+ELC0toBgsOFBRrSRYZLTQLC+ELC0toBgsIDA8LGQoFGwUxDxclIyQHHSYjLxMSIhsjJAcdJiMv\ +ExIiGyMkBx0mIx4ta0wCBRIyDg0PAgQSNQsP/ssCBRIyDg0PAgQSNQsP/ssCBRIyDg0PAgQSNQsP\ +IHoeHgEdEgwPGCMBR20geh4eAR0SDA8YIwFHbSB6Hh4BHRIMDwcUEiwQCA8JWBAZExoeDzIcFhMa\ +Hg8yHBYTGh4PNDBHbSgBNSQhSgUBMxwnJCkGATUkIUoFATMcJyQpBgE1JCAmJQUBMxwnJCkAAAP/\ +tP+IAsEBGABMAF4AbwAAMyInBzMyFCsBIjQ7ARM2NTQjIg4DBwYmNzY3NjMyFhc+ATMyFz4BMzIW\ +Fz4BMzIWFRQGIyInBzMyFCsBIjQ7ARM2NTQjIgYHFRQGJxYzMjY3NjU0JyYjIgYHBhUUBRYzMjY3\ +NjQnJiMiBgcGFRTIFhktNAsL4QsLS2gGCwgMDwsZCgUbBTEPFyUjJAcdJiMvExIiGyMkBx0mIx4t\ +a0kWGS00CwvhCwtLaAYLDhQUa0wCBRIyDg0PAgQSNQsPAVUCBRIyDg0PAgQSNQsPIHoeHgEdEgwP\ +BxQSLBAIDwlYEBkTGh4PMhwWExoeDzQwR20geh4eAR0SDA8YIwFHbSgBNSQgJiUFATMcJyQpBgE1\ +JCFKBQEzHCckKQAAAAL/2/+IAysBGAByAIMAACU2NzYzMhYXPgEzMhYVFAYjIicHMzIUKwEiNDsB\ +EzY1NCMiDgMHDgEjIiY1ND8BNjU0IyIPAQYHJwYmPwE2NTQjIg8BBgcGKwEiPwE2JiMiBgcGJjc+\ +AzMyFzYzMhc+ATMyFhUUDwEGFRQzMjc+ARcWMzI2NzY0JyYjIgYHBhUUAa0xFBgiIyQHHSYjHi1r\ +SRYZLTQLC+ELC0toBgsHDxIOGAgaPC0VGQU0ARUbCEEIDyYNCARDARUbCEQHCAQENQ0NQgQGCA0W\ +JAUVBBQQIh8SNwskJC0JCykTGSQFLwQIAQQPJdECBRIyDg0PAgQSNQsPd2wYHRMaHg80MEdtIHoe\ +HgEdEgwPDR8bNRA2OBYTDQ2IAgQOFKsWAQEBDwirAgQOFKwRAwEapw0PGjkIDAklGjASKCgoEBgj\ +GgsPfQsJDgIINTkBNSQhSgUBMxwnJCkAAAAAAv/b/2ADGQG4AFMAlQAANwYHBisBIj8BNiYjIgYH\ +BiY3PgMzMhc2MzIXPgEzMhYVFA8BBhUUMzI3PgU3NhYHDgIjIiY1ND8BNjU0IyIPAQYHJwYmPwE2\ +NTQjIgcTIiY1NDYzMhYVFAcGFRQzMj4HNyMiNTQ7AT4BMzIWFRQGIyImNTQ3NjQjIg4HBzMyFCsB\ +DgFQBwgEBDUNDUIEBggNFiQFFQQUECIfEjcLJCQtCQspExkkBS8ECAEEBQsHDAMNAQYVBhIXLR8V\ +GQU0ARUbCEEIDyYNCARDARUbCPUgMBcTEhcSChkLEA8LDQoODRQKNRMRQRRpNCAwFxMSFxIKGQcM\ +CgcIBQYDBgE2FBQ/IXYVEQMBGqcNDxo5CAwJJRowEigoKBAYIxoLD30LCQ4CAwkGDwUSAQkNCx8f\ +GhYTDQ2IAgQOFKsWAQEBDwirAgQOFP6fJiAaIhQPDgsHDQ4GERMlJDw3VScVE0tfJiAaIhQPDgsG\ +HAULChQNGg0eBijFwQAAAAH/fv9gAmkBuAB0AAAlIw4BIyImNTQ2MzIWFRQHBhUUMzI+BzcjIjU0\ +OwE+ATMyFhUUBiMiJjU0NzY0IyIHBgcXPgEzMhYVFAYjIiY1NDc2NCMiDgcHMzIUKwEOASMiJjU0\ +NjMyFhUUBwYVFDMyPgcBX5shdl8gMBcTEhcSChkLEA8LDQoODRQKNRMRQRRpNCAwFxMSFxIKGSYX\ +AwGbFGk0IDAXExIXEgoZBwwKBwgFBgMGATYUFD8hdl8gMBcTEhcSChkLEA8LDQoPDRTmxcEmIBoi\ +FA8OCwcNDgYREyUkPDdVJxUTS18mIBoiFA8OCwYccwwGAUtfJiAaIhQPDgsGHAULChQNGg0eBijF\ +wSYgGiIUDw4LBw0OBhETJSQ8N1UAAAAB/37/YAN0AbgAqgAAEzM+ATMyFhUUBiMiJjU0NzY0IyIH\ +BgcXPgEzMhYVFAYjIiY1NDc2NCMiDgcHMzIUKwEOASMiJjU0NjMyFhUUBwYVFDMyPgc3Iw4BIyIm\ +NTQ2MzIWFRQHBhUUMzI+BzcjDgEjIiY1NDYzMhYVFAcGFRQzMj4HNyMiNTQ7AT4BMzIWFRQGIyIm\ +NTQ3NjQjIg4CBwbMnBRpNCAwFxMSFxIKGSYXAwGbFGk0IDAXExIXEgoZBwwKBwgFBgMGATYTEz8h\ +dl8gMBcTEhcSChkLEA8LDQoPDRQKmyF2XyAwFxMSFxIKGQsQDwsNCg4NFQqbIXZfIDAXExIXEgoZ\ +CxAPCw0KDg0UCjUTEUEUaTQgMBcTEhcSChkOFQ4IBgIBDktfJiAaIhQPDgsGHHMMBgFLXyYgGiIU\ +Dw4LBhwFCwoUDRoNHgYoxcEmIBoiFA8OCwcNDgYREyUkPDdVJ8XBJiAaIhQPDgsHDQ4GERMlJDs4\ +VSfFwSYgGiIUDw4LBw0OBhETJSQ8N1UnFRNLXyYgGiIUDw4LBhwTJyIbCgAB/37/YASAAbgA4AAA\ +ARc+ATMyFhUUBiMiJjU0NzY0IyIHBgcXPgEzMhYVFAYjIiY1NDc2NCMiDgcHMzIUKwEOASMiJjU0\ +NjMyFhUUBwYVFDMyPgc3Iw4BIyImNTQ2MzIWFRQHBhUUMzI+BzcjDgEjIiY1NDYzMhYVFAcGFRQz\ +Mj4HNyMOASMiJjU0NjMyFhUUBwYVFDMyPgc3IyI1NDsBPgEzMhYVFAYjIiY1NDc2NCMiBwYHFz4B\ +MzIWFRQGIyImNTQ3NjQjIg4HAdicFGk0IDAXExIXEgoZJhcDAZsUaTQgMBcTEhcSChkHDAoHCAUG\ +AwYBNhMTPyF2XyAwFxMSFxIKGQsQDwsNCg8NFAqbIXZfIDAXExIXEgoZCxAPCw0KDg0UCpshdl8g\ +MBcTEhcSChkLEA8LDQoPDRQKmyF2XyAwFxMSFxIKGQsQDwsNCg4NFAo1ExFBFGk0IDAXExIXEgoZ\ +JhcDAZsUaTQgMBcTEhcSChkHDAoHCAUGAwYBDwFLXyYgGiIUDw4LBhxzDAYBS18mIBoiFA8OCwYc\ +BQsKFA0aDR4GKMXBJiAaIhQPDgsHDQ4GERMlJDw3VSfFwSYgGiIUDw4LBw0OBhETJSM8N1YnxcEm\ +IBoiFA8OCwcNDgYREyUkPDdVJ8XBJiAaIhQPDgsHDQ4GERMlJDw3VScVE0tfJiAaIhQPDgsGHHMM\ +BgFLXyYgGiIUDw4LBhwFCgsTDhkOHQAAAAADAAD/YALfAbgANgBxALYAADMiJjU0NjMyFhUUBxYz\ +MjY1NC4DJy4CNTQ2MzIWFRQGIyImNTQ3JiMiBhUUHgIXHgEVFCUGLgE/ASYjIgYjIg4BBwYnJjc+\ +ATcWMzI2MzIXFhQPAQYVFB4BFxY+AScuATU0NjMyFRQGKwEuASMiBSImNTQ2MzIWFRQHDgEeARUU\ +FjI+BzcjIjU0OwE+ATMyFhUUBiMiJjU0NzY0IyIOBwczMhQrAQ4BUB4yEgwOFwwGGBYhAwwFFwMU\ +GBQ3LSI2FhANFgUQDg8ZERIeBRsXASYGCwEFrgoUBRoJBRASBRAFAwcLDAExGiAoBwIUCweaBBoq\ +DQUJBQIDJBQMJisgBhc1CRP+tCAwFxMSFxIEAQECBhYQDwsNCg4NFAo1ExFBFGk0IDAXExIXEgoZ\ +BwwKBwgFBgMGATYTEz8hdisbEBYNCxgMEhYSCQwNBA4CDRIeDyMtJBgQGBQOBgoUEQ0JEwwRAxIf\ +FVoDBAgNBr8FARgfBA0LCBMeLgQDCAIBEQejBQUHAwcLAwQNBggIFAsTNx4qAg2yJiAaIhQPDgsC\ +BQQGAwgGBhETJSQ8N1UnFRNLXyYgGiIUDw4LBhwFCwoUDRoNHgYoxcEAAgAF//sB/AGaAAkALAAA\ +AQ8BBhUUMzI2NwcOASMiJjU0PwEjNTM/AQc3MhU2MzIWFRQGIiY1NDcGDwEjATZ/OQIUGEQSDCky\ +HyIiAzdpcxZZJpgZIDUYHRggFgk3EEJIAQkHuAgDFRcPKBkUJBoLC7MgSi54Ci0pHBUSGxEOEhMP\ +J9gAAQAA//0BtQDUAC8AACUyNTQnBiImNT4BMzIWFRQHBiMiLwEmIyIVFBc2MzIWFRQGByInJjU0\ +NzYzFh8BFgFwLRoQHBQBFwkkLSkXHiYeohoRLhkQDw0VFA0dGBwoFiIqF6IcMTkgExAWDQ4WMzUz\ +JBUVehI6IBIQFw4PEgIaIi0zJBMCEHoTAAEAAP/NAbUBAwA2AAAXIiY1NDc2MxYfATUzFRcWMzI2\ +NTQnBiMiNT4BMzIWFRQHBiMiLwEVIzUnJiMiBhUUFzYzMhUUTSAtKBYgKBcuHlwiDxUcFhQSHAEP\ +CSAtKRccJB4uHlwgDxYcFRQTHANDJjMkEwIQJGmARxcpHCcQDBsPFUImMyQVFSRsg0cWKB4nDwwd\ +IAABAA0AAAJFAOAACwAANyc3FzcXNxcHJwcnIhWOZXhqTRaSaXRpLhmZfHx8VBehfHx8AAAAAQAN\ +/8sCRQERABMAACUHJwcnNxc3NTMXNxc3FwcnBxUjARZAaUsVjmUWGwFGak8UkmkZG0ZGfE4ZmXwX\ +lntKfFUYoXwblgAAAQAAAAABGAEYAAsAADM1IzUzNTMVMxUjFXt7eyJ7e3sie3siewAAAAEAAAAA\ +ATYBcgAKAAAxNT4ENzMUBiQxTDQ0Dx7APAcPKz1vSY7aAAAB//8AAAEtAKAAHQAANz4CMzIeARcW\ +MzI3NhYHDgIjIi4BJyYjIgcGJgEJECkcGCYmDwkKHhgEDgIIESkcGCQlEgcIHR0FDUYZISAhMQoG\ +JAYHBxkiHyExCgQjBggAAAABAAAAAAEsASwABwAAMREhESM1IxUBLCPmASz+1LS0AAEAAAAAAPoB\ +wgAGAAAzAzMbATMDaWkoVVUoaQHC/pgBaP4+AAIAAAAAAMgAyAAHAA8AADYyNjQmIgYUFiImNDYy\ +FhRFPiwsPix0Ujs7UjsZLD4sLD5FO1I7O1IAAf84AAAAyADIAAsAACM0NjIWFSM0JiIGFch2pHYe\ +YJRgUnZ2UkpgYEoAAAACAAAAAAC0ASwABwAVAAA2MjY0JiIGFBc1LgE1NDYyFhUUBgcVSx4bGx4b\ +GB0rN0Y3Kx14NTY1NTatZAg1Jyg8PCgnNQhkAAACAAAAAADIASwADwAfAAA3LgE1NDYyFhUUBgcd\ +ASM1Nz4BNTQmIgYVFBYXPQEzFVQkMDtSOzAkICAZIiw+LCIZIGYFOCUpOzspJTgFAWVlGQYpGx8s\ +LB8bKQYBSUkAAAAEAAD//AP0An8AgwCNAJkAowAANz4BNTQnLgE1ND4CPwIOARUUMzI3Fw4BIyIm\ +NTQ+AjMyFhUUBiMiJic3HgEzMjU0LgInBwYVFB4CFRQGDwEeAjsBMjcmNTQ3NjMyFhUUBwYHHgEz\ +MjY1NDY3LgI9AR4BFRQGIyInBiMiLgEnDgEiJicmIyIOAQcGIyI1NDYFIiY0NjMyFhQGJT4BNTQn\ +JiMiBhUUBTY1NCYnDgEVFJg2RQUDUgMEBQECPWJqIB4dGhUpJR4tHz5wSH1yMi4cNhIYERQUMwwc\ +QC0bCR0iHR8QEA4pHwwNESIhHCJMGyMKHTwTIBsWMUNdIIFeurRRQkEjJDwhMxYOLCggGxwaEgoU\ +GQZaIQowA1wLExMLDBIR/g8zJAQFFRwuATxaFxI/MFoYPyAKDAh0IgUODgwEBKUEYywjPA1CLyYa\ +IUdELFE3MDUwKw4eETIKGiIaAkIkGx42ISwVJk8UFAopGh41KDUlLisgIRM5MBcSKhlSXxksWTQG\ +ATyxa0ZfOToVFBImGRYgLQsVBDoHDThPEhgSEhgSgC8vGwcOEjkrHnQFgSJJFR1YRD8AAAIADAAK\ +AdMBzwAKAI8AACU0JiMiBhQWMzI2Jw4BIyImNDYzMhYXNjU0JyYjIiY0NjMyFx4BFxYzMjU0Jy4B\ +NTQ2MzIWFRQGBxQzMjc+ATc2MzIWFRQGIyIGBwYVFDMyNjMyFhQGIyImIyIGFRQXHgEXFhUUBiMi\ +Jy4BJyYjIhUUFhUUBiMiJjQ2NTQjIgcOAQcGIyImNTQ3NjMyNzY1NAEWGA8QFRYPEBeJGSYOGRsa\ +GQ0qGCUMFBgdHBkXEhALAxQLEhQBAiceFBIbIwEWEA0RAQ0MGhMeGxIbFQ0QIRsrDhwbHRkPJxQX\ +EgsUOA0OGRcTFAwBExEKEiogExIdJhcNDhIDERIMFBoNDBYjEg3uEBQTIhYVCgElGSobJQIDFg4K\ +ExwoHQwLPhYNGA0IFycPFxobFhEjGSoQEzgPDhsUESMGCw4PFicdKBomCAoWChIDCw0ZExsOCzkV\ +Dx8eMBMUGBokMRckDhI9DAcWFBgODRINDBoAAAADAAD/BgH0APoABwAPABcAADYUFjI2NCYiAjQ2\ +MhYUBiI2IiY0NjIWFC14qnh4qqWS0JKS0IEyIyMyI1WqeHiqeP7L0JKS0JK+IzIjIzIAAAIAAP8G\ +AfQA+gAHAA8AADYUFjI2NCYiAjQ2MhYUBiIteKp4eKqlktCSktBVqnh4qnj+y9CSktCSAAAAAAMA\ +AP6iAfQBXgARABcAHQAAEzMVHgEVFAYHFSM1LgE1NDY3GQEOARQWFz4BNCYn5C1gg4NgLWCEg2FO\ +aWl7TWlpTQFeZAqOYmGPCmRlCI9iY44I/jsBmAh1nnUICHacdggAAAACAAD/BgHSAPoAIQApAAAl\ +FhUUBwYjIicmIyIGFBYzMjc2MzIXFhUUBwYjIiY0NjMyAiImNDYyFhQBzwILBQYNCjN3VXh4VXgy\ +BhIHAwwDQJVokpJolHsyIyMyI4IGBA8GAw5feKp4Xw0CBwwGBniS0JL+yiMyIyMyAAAAAQAA/wYB\ +0gD6ACEAACUWFRQHBiMiJyYjIgYUFjMyNzYzMhcWFRQHBiMiJjQ2MzIBzwILBQYNCjN3VXh4VXgy\ +BhIHAwwDQJVokpJolIIGBA8GAw5feKp4Xw0CBwwGBniS0JIAAAIAAP6iAdIBXgAkACoAACUWFxYV\ +FAcGIyInJicRNjc2MzIXFhUUBwYHFSM1LgE1NDY3NTMDEQ4BFBYBEYQ6AgsFBg0KLWZlLgYSBwMM\ +AzyCLWCEg2EtLU5pafkKbQYEDwYDDlQK/mgHVw0CBwwGBnAHZWUIj2Jjjghl/dYBmAh1nnUAAAEA\ +AP/EAHgAPAAHAAAWIiY0NjIWFFUyIyMyIzwjMiMjMgAAAAEAAP6iAC0BXgADAAATESMRLS0BXv1E\ +ArwAAAACAAD9EgFKAL4AAwAPAAA3FSE1JTMVITUzESMRIRUjHgEO/tQeAQ4eHv7yHkaMjHgyMvxU\ +AmIyAAEAAAAAAlYBcgALAAA1Nxc3FzcXAScHJweJVFZSryL+/FRWUzVBuXNzcekW/qR0dHBHAAAB\ +/+gAvQESATcAFwAAJj4BMzIWMjc2MzIVFA4BIyImIgcGIyI1GDAhHBRVKBQDBw4wIRwUVSgUBAcN\ +9jARPRQDBwwwET0UBAgAAAAAAgAA/4kBwgCLAAsAEwAABTQmIyIGFRQWMzI+ARQGIiY0NjIBUVo2\ +IDRdNyEvcX3IfX3IFDRPJx80TCN0bEtLbEsAAAACAAD/bAFIAqgADQAcAAAlJiMiBhUUFxYzMjY1\ +NBMzERQGIyInJjU0NjMyFwEkDSU8lwYLJjyXAh1+SE8kD35IQCREF2ErCgkXYSsJAm79bkNnQx4d\ +Q2ctAAAAAAEAAP95AUACqAANAAABMxEUBiMiJjU0NjMyFwEiHnlZMjx6WDEfAqj9gEZpOCdFahwA\ +AAABAAD/eQIKAqgAGgAAAREUBiMiJjU0NjMyFxE1Mx4EFRQHNjU0AUB5WTI8elgxHx4GLjk4JTIS\ +Abn+b0ZpOCdFahwBTu81XEdOdUlIckFJ7wAAAAACAAD/eQILAqgAHwAoAAAlNREzHgQVFAcWFRQH\ +NjU0JiMRFAYjIiY1NDYzMhMeARc0NjU0JgEiHgkwODYjFRYiBXY4eVkyPHpYMT0OhRsBdmvmAVck\ +RztCWDMlMjAkLkEZIV+e/tZGaTgnRWoBdjuiNQMOA1+fAAABAAD/zgBkADIABwAAFiImNDYyFhRH\ +Kh0dKh0yHSodHSoAAAAAAA4ArgABAAAAAAAAAIMBCAABAAAAAAABAAcBnAABAAAAAAACAAcBtAAB\ +AAAAAAADAB8B/AABAAAAAAAEAAcCLAABAAAAAAAFAAgCRgABAAAAAAAGAAcCXwADAAEECQAAAQYA\ +AAADAAEECQABAA4BjAADAAEECQACAA4BpAADAAEECQADAD4BvAADAAEECQAEAA4CHAADAAEECQAF\ +ABACNAADAAEECQAGAA4CTwBDAG8AcAB5AHIAaQBnAGgAdAAgAFwAMgA1ADEAIAAyADAAMQA4AC0A\ +MgAwADEAOQAgAEoAZQBhAG4ALQBGAHIAYQBuAGMAbwBpAHMAIABNAG8AaQBuAGUALgAgAFQAaABp\ +AHMAIABmAG8AbgB0ACAAaQBzACAAbABpAGMAZQBuAHMAZQBkACAAdQBuAGQAZQByACAAdABoAGUA\ +IABTAEkATAAgAE8AcABlAG4AIABGAG8AbgB0ACAATABpAGMAZQBuAHMAZQAgAFwAKABoAHQAdABw\ +ADoALwAvAHMAYwByAGkAcAB0AHMALgBzAGkAbAAuAG8AcgBnAC8ATwBGAEwAXAApAC4AAENvcHly\ +aWdodCBcMjUxIDIwMTgtMjAxOSBKZWFuLUZyYW5jb2lzIE1vaW5lLiBUaGlzIGZvbnQgaXMgbGlj\ +ZW5zZWQgdW5kZXIgdGhlIFNJTCBPcGVuIEZvbnQgTGljZW5zZSBcKGh0dHA6Ly9zY3JpcHRzLnNp\ +bC5vcmcvT0ZMXCkuAABhAGIAYwAyAHMAdgBnAABhYmMyc3ZnAABSAGUAZwB1AGwAYQByAABSZWd1\ +bGFyAABGAG8AbgB0AEYAbwByAGcAZQAgADoAIABhAGIAYwAyAHMAdgBnACAAOgAgADEANAAtADQA\ +LQAyADAAMgAzAABGb250Rm9yZ2UgOiBhYmMyc3ZnIDogMTQtNC0yMDIzAABhAGIAYwAyAHMAdgBn\ +AABhYmMyc3ZnAABWAGUAcgBzAGkAbwBuACAAAFZlcnNpb24gAABhAGIAYwAyAHMAdgBnAABhYmMy\ +c3ZnAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJIAAAABAAIBAgADAQMBBAEF\ +AQYBBwEIAQkBCgELAQwBDQEOAQ8BEAERARIBEwEUARUBFgEXARgBGQEaARsBHAEdAR4BHwEgASEB\ +IgEjASQBJQEmAScBKAEpASoBKwEsAS0BLgEvATABMQEyATMBNAE1ATYBNwE4ATkBOgE7ATwBPQE+\ +AT8BQAFBAUIBQwFEAUUBRgFHAUgBSQFKAUsBTAFNAU4BTwFQAVEBUgFTAVQBVQFWAVcBWAFZAVoB\ +WwFcAV0BXgFfAWABYQFiAWMBZAFlAWYBZwFoAWkBagFrAWwBbQFuAW8BcAFxAXIBcwF0AXUBdgF3\ +AXgBeQF6AXsBfAF9AX4BfwGAAYEBggGDAYQBhQGGAYcBiAGJAYoBiwGMAY0BjgGPBi5ub2RlZgd1\ +bmlFMDAwB3VuaUUwMzAHdW5pRTAzOAd1bmlFMDM5B3VuaUUwNDMHdW5pRTA0NQd1bmlFMDQ2B3Vu\ +aUUwNDcHdW5pRTA0OAd1bmlFMDUwB3VuaUUwNUMHdW5pRTA2Mgd1bmlFMDY5B3VuaUUwN0EHdW5p\ +RTA3Qgd1bmlFMDdDB3VuaUUwN0QHdW5pRTA3RQd1bmlFMDgwB3VuaUUwODEHdW5pRTA4Mgd1bmlF\ +MDgzB3VuaUUwODQHdW5pRTA4NQd1bmlFMDg2B3VuaUUwODcHdW5pRTA4OAd1bmlFMDg5B3VuaUUw\ +OEEHdW5pRTA4Qgd1bmlFMDhDB3VuaUUwOTQHdW5pRTA5NQd1bmlFMEEwB3VuaUUwQTEHdW5pRTBB\ +Mgd1bmlFMEEzB3VuaUUwQTQHdW5pRTBBOQd1bmlFMEIzB3VuaUUxMDEHdW5pRTFCOQd1bmlFMUJC\ +B3VuaUUxRTcHdW5pRTFGMgd1bmlFMUY0B3VuaUUxRjcHdW5pRTFGOQd1bmlFMUZDB3VuaUUxRkUH\ +dW5pRTFGRgd1bmlFMjAwB3VuaUUyNDAHdW5pRTI0MQd1bmlFMjQyB3VuaUUyNDMHdW5pRTI0NAd1\ +bmlFMjQ1B3VuaUUyNDYHdW5pRTI0Nwd1bmlFMjQ4B3VuaUUyNDkHdW5pRTI2MAd1bmlFMjYxB3Vu\ +aUUyNjIHdW5pRTI2Mwd1bmlFMjY0B3VuaUUyNmEHdW5pRTI2Ygd1bmlFMjgwB3VuaUUyODEHdW5p\ +RTI4Mgd1bmlFMjgzB3VuaUU0QTAHdW5pRTRBMgd1bmlFNEE0B3VuaUU0QTgHdW5pRTRBQwd1bmlF\ +NEMwB3VuaUU0Q0UHdW5pRTREMQd1bmlFNEUxB3VuaUU0RTIHdW5pRTRFMwd1bmlFNEU0B3VuaUU0\ +RTUHdW5pRTRFNgd1bmlFNEU3B3VuaUU0RTgHdW5pRTRFOQd1bmlFNEVBB3VuaUU1MDAHdW5pRTUw\ +MQd1bmlFNTIwB3VuaUU1MjEHdW5pRTUyMgd1bmlFNTIzB3VuaUU1MjQHdW5pRTUyNQd1bmlFNTI5\ +B3VuaUU1MkEHdW5pRTUyQgd1bmlFNTJDB3VuaUU1MkQHdW5pRTUyRgd1bmlFNTMwB3VuaUU1MzEH\ +dW5pRTUzOQd1bmlFNTY2B3VuaUU1NjcHdW5pRTU2OQd1bmlFNTZDB3VuaUU1NkQHdW5pRTU4Mgd1\ +bmlFNUQwB3VuaUU1RTIHdW5pRTYxMAd1bmlFNjEyB3VuaUU2MTQHdW5pRTYxOAd1bmlFNjI0B3Vu\ +aUU2MzAHdW5pRTY1MAd1bmlFNjU1B3VuaUU5MTAHdW5pRTkxMQd1bmlFOTEyB3VuaUU5MTQHdW5p\ +RTkxNQd1bmlFOTE4B3VuaUU5MjAHdW5pRTkyNQd1bmlFOTVEB3VuaUVBMDIHdW5pRUFBNAd1bmlF\ +Q0EyB3VuaUVDQTMHdW5pRUNBNQd1bmlFQ0E3B3VuaUVDQTkHdW5pRUNCNwAAAAAAAAH//wACAAEA\ +AAAAAAAADAAUAAQAAAACAAAAAQAAAAEAAAAAAAEAAAAA399K6gAAAADRlyIXAAAAAOBezeg=\ +") format("truetype")' +var font_scale_tb={serif:1,serifBold:1,'sans-serif':1,'sans-serifBold':1,Palatino:1.1,monospace:1},txt_ff="text,serif",fmt_lock={} +var cfmt={"abc-version":"1",annotationfont:{name:"text,sans-serif",size:12},aligncomposer:1,beamslope:.4,bardef:{"[":"","[]":"","|:":"[|:","|::":"[|::","|:::":"[|:::",":|":":|]","::|":"::|]",":::|":":::|]","::":":][:"},breaklimit:.7,breakoneoln:true,cancelkey:true,composerfont:{name:txt_ff,style:"italic",size:14},composerspace:6,decoerr:true,dynalign:true,footerfont:{name:txt_ff,size:16},fullsvg:'',gchordfont:{name:"text,sans-serif",size:12},gracespace:new Float32Array([6,8,11]),graceslurs:true,headerfont:{name:txt_ff,size:16},historyfont:{name:txt_ff,size:16},hyphencont:true,indent:0,infofont:{name:txt_ff,style:"italic",size:14},infoname:'R "Rhythm: "\n\ +B "Book: "\n\ +S "Source: "\n\ +D "Discography: "\n\ +N "Notes: "\n\ +Z "Transcription: "\n\ +H "History: "',infospace:0,keywarn:true,leftmargin:1.4*CM,lineskipfac:1.1,linewarn:true,maxshrink:.65,maxstaffsep:2000,maxsysstaffsep:2000,measrepnb:1,measurefont:{name:txt_ff,style:"italic",size:10},measurenb:-1,musicfont:{name:"music",src:musicfont,size:24},musicspace:6,partsfont:{name:txt_ff,size:15},parskipfac:.4,partsspace:8,pagewidth:21*CM,"propagate-accidentals":"o",printmargin:0,rightmargin:1.4*CM,rbmax:4,rbmin:2,repeatfont:{name:txt_ff,size:9},scale:1,slurheight:1.0,spatab:new Float32Array([10.2,13.3,17.3,22.48,29.2,38,49.4,64.2,83.5,108.5]),staffsep:46,stemheight:21,stretchlast:.25,stretchstaff:true,subtitlefont:{name:txt_ff,size:16},subtitlespace:3,sysstaffsep:34,systnames:-1,tempofont:{name:txt_ff,weight:"bold",size:12},textfont:{name:txt_ff,size:16},textspace:14,tieheight:1.0,titlefont:{name:txt_ff,size:20},titlespace:6,titletrim:true,topspace:22,tuplets:[0,0,0,0],tupletfont:{name:txt_ff,style:"italic",size:10},vocalfont:{name:txt_ff,weight:"bold",size:13},vocalspace:10,voicefont:{name:txt_ff,weight:"bold",size:13},writefields:"CMOPQsTWw",wordsfont:{name:txt_ff,size:16},wordsspace:5,"writeout-accidentals":"n"} +var sfmt={bardef:true,barsperstaff:true,beamslope:true,breaklimit:true,bstemdown:true,cancelkey:true,dynalign:true,flatbeams:true,gracespace:true,hyphencont:true,keywarn:true,maxshrink:true,maxstaffsep:true,measrepnb:true,rbmax:true,rbmin:true,shiftunison:true,slurheight:true,squarebreve:true,staffsep:true,systnames:1,stemheight:true,stretchlast:true,stretchstaff:true,tieheight:true,timewarn:true,trimsvg:1,vocalspace:true} +function get_bool(param){return!param||!/^(0|n|f)/i.test(param)} +function get_font_scale(param){var i,font,a=info_split(param) +if(a.length<=1) +return +var scale=+a[a.length-1] +if(isNaN(scale)||scale<=0.5){syntax(1,"Bad scale value in %%font") +return} +font_scale_tb[a[0]]=scale} +function set_font_fac(font){var scale=font_scale_tb[font.fname||font.name] +if(!scale) +scale=1.1;font.swfac=font.size*scale} +function param_set_font(xxxfont,p){var font,n,a,ft2,k +if(xxxfont[xxxfont.length-2]=='-'){n=xxxfont[xxxfont.length-1] +if(n<'1'||n>'9') +return +xxxfont="u"+n+"font"} +font={} +a=p.match(/\s+(no)?box(\s|$)/) +if(a){if(a[1]){font.box=false +font.pad=0}else{font.box=true +font.pad=2.5} +p=p.replace(a[0],a[2])} +a=p.match(/\s+padding=([\d.]+)(\s|$)/) +if(a){font.pad=a[1]?+a[1]:0 +p=p.replace(a[0],a[2])} +a=p.match(/\s+class=(.*?)(\s|$)/) +if(a){font.class=a[1];p=p.replace(a[0],a[2])} +a=p.match(/\s+wadj=(.*?)(\s|$)/) +if(a){if(typeof document=="undefined") +switch(a[1]){case'none':font.wadj='' +break +case'space':font.wadj='spacing' +break +case'glyph':font.wadj='spacingAndGlyphs' +break +default:syntax(1,errs.bad_val,"%%"+xxxfont) +break} +p=p.replace(a[0],a[2])} +a=p.match(/\s+([0-9.]+|\*)$/) +if(a){if(a[1]!="*") +font.size=+a[1] +p=p.replace(a[0],"")} +if((p[0]=='u'&&p.slice(0,4)=="url(")||(p[0]=='l'&&p.slice(0,6)=="local(")){n=p.indexOf(')',1) +if(n<0){syntax(1,"No end of url in font family") +return} +font.src=p.slice(0,n+1) +font.fid=abc2svg.font_tb.length +abc2svg.font_tb.push(font) +font.name='ft'+font.fid +p=p.replace(font.src,'')} +a=p.match(/[- ]?[nN]ormal/) +if(a){font.normal=true +p=p.replace(a[0],'')} +a=p.match(abc2svg.ft_re) +if(a){font.weight=abc2svg.ft_w[a[0].replace(/[ -]/,'').toLowerCase()] +p=p.replace(a[0],'')} +a=p.match(/[- ]?[iI]talic/) +if(a){font.style="italic" +p=p.replace(a[0],'')} +a=p.match(/[- ]?[oO]blique/) +if(a){font.style="oblique" +p=p.replace(a[0],'')} +if(!font.src){if(p[0]=='"'){n=p.indexOf('"',1) +if(n<0){syntax(1,"No end of string in font family") +return} +p=p.slice(1,n)} +p=p.trim() +switch(p){case"":case"*":p="";break +case"Times-Roman":case"Times":p="serif";break +case"Helvetica":p="sans-serif";break +case"Courier":p="monospace";break +case"music":p=cfmt.musicfont.name;break +default:if(p.indexOf("Fig")>0) +font.figb=true +break}} +if(p&&!font.name) +font.name=p +if(font.size) +set_font_fac(font) +if(!font.name||!font.size){ft2=cfmt[xxxfont] +for(k in ft2){if(!ft2.hasOwnProperty(k)||font[k]!=undefined) +continue +switch(k){case"fid":case"used":case"src":break +case"style":case"weight":if(font.normal) +break +default:font[k]=ft2[k] +break}} +if(!font.swfac) +set_font_fac(font)} +if(font.pad==undefined) +font.pad=0 +font.fname=font.name +if(font.weight>=700) +font.fname+='Bold' +cfmt[xxxfont]=font} +function get_unit(param){var v=param.toLowerCase().match(/(-?[\d.]+)(.*)/) +if(!v) +return NaN +v[1]=+v[1] +switch(v[2]){case"cm":return v[1]*CM +case"in":return v[1]*IN +case"pt":return v[1]/.75 +case"px":case"":return v[1]} +return NaN} +function set_infoname(param){var tmp=cfmt.infoname.split("\n"),letter=param[0] +for(var i=0;i0) +v=v.slice(0,i) +return textopt[v]} +var posval={above:C.SL_ABOVE,auto:0,below:C.SL_BELOW,down:C.SL_BELOW,hidden:C.SL_HIDDEN,opposite:C.SL_HIDDEN,under:C.SL_BELOW,up:C.SL_ABOVE} +function set_pos(k,v){k=k.slice(0,3) +if(k=="ste") +k="stm" +set_v_param("pos",'"'+k+' '+v+'"')} +function set_writefields(parm){var c,i,a=parm.split(/\s+/) +if(get_bool(a[1])){for(i=0;i=0) +cfmt.writefields=cfmt.writefields.replace(c,'')}}} +function set_v_param(k,v){k=[k+'=',v] +if(parse.state<3) +memo_kv_parm(curvoice?curvoice.id:'*',k) +else if(curvoice) +set_kv_parm(k) +else +memo_kv_parm('*',k)} +function set_page(){if(!img.chg) +return +img.chg=false;img.lm=cfmt.leftmargin-cfmt.printmargin +if(img.lm<0) +img.lm=0;img.rm=cfmt.rightmargin-cfmt.printmargin +if(img.rm<0) +img.rm=0;img.width=cfmt.pagewidth-2*cfmt.printmargin +if(img.width-img.lm-img.rm<100){error(0,undefined,"Bad staff width");img.width=img.lm+img.rm+150} +set_posx()} +Abc.prototype.set_format=function(cmd,param){var f,f2,v,i +if(/.+font(-[\d])?$/.test(cmd)){if(cmd=="soundfont") +cfmt.soundfont=param +else +param_set_font(cmd,param) +return} +if(sfmt[cmd]&&parse.ufmt) +cfmt=Object.create(cfmt) +switch(cmd){case"aligncomposer":case"barsperstaff":case"infoline":case"measurenb":case"rbmax":case"rbmin":case"measrepnb":case"shiftunison":case"systnames":v=parseInt(param) +if(isNaN(v)){syntax(1,"Bad integer value");break} +cfmt[cmd]=v +break +case"abc-version":case"bgcolor":case"fgcolor":case"propagate-accidentals":case"titleformat":case"writeout-accidentals":cfmt[cmd]=param +break +case"beamslope":case"breaklimit":case"lineskipfac":case"maxshrink":case"pagescale":case"parskipfac":case"scale":case"slurheight":case"stemheight":case"tieheight":f=+param +if(isNaN(f)||!param||f<0){syntax(1,errs.bad_val,'%%'+cmd) +break} +switch(cmd){case"scale":f/=.75 +case"pagescale":if(f<.1) +f=.1 +cmd="scale";img.chg=true +break} +cfmt[cmd]=f +break +case"annotationbox":case"gchordbox":case"measurebox":case"partsbox":param_set_font(cmd.replace("box","font"),"* * "+(get_bool(param)?"box":"nobox")) +break +case"altchord":case"bstemdown":case"breakoneoln":case"cancelkey":case"checkbars":case"contbarnb":case"custos":case"decoerr":case"flatbeams":case"graceslurs":case"graceword":case"hyphencont":case"keywarn":case"linewarn":case"quiet":case"squarebreve":case"splittune":case"straightflags":case"stretchstaff":case"timewarn":case"titlecaps":case"titleleft":case"trimsvg":cfmt[cmd]=get_bool(param) +break +case"dblrepbar":param=":: "+param +case"bardef":v=param.split(/\s+/) +if(v.length!=2){syntax(1,errs.bad_val,"%%bardef")}else{if(parse.ufmt) +cfmt.bardef=Object.create(cfmt.bardef) +cfmt.bardef[v[0]]=v[1]} +break +case"chordalias":v=param.split(/\s+/) +if(!v.length) +syntax(1,errs.bad_val,"%%chordalias") +else +abc2svg.ch_alias[v[0]]=v[1]||"" +break +case"composerspace":case"indent":case"infospace":case"maxstaffsep":case"maxsysstaffsep":case"musicspace":case"partsspace":case"staffsep":case"subtitlespace":case"sysstaffsep":case"textspace":case"titlespace":case"topspace":case"vocalspace":case"wordsspace":f=get_unit(param) +if(isNaN(f)) +syntax(1,errs.bad_val,'%%'+cmd) +else +cfmt[cmd]=f +break +case"page-format":user.page_format=get_bool(param) +break +case"print-leftmargin":syntax(0,"$1 is deprecated - use %%printmargin instead",'%%'+cmd) +cmd="printmargin" +case"printmargin":case"leftmargin":case"pagewidth":case"rightmargin":f=get_unit(param) +if(isNaN(f)){syntax(1,errs.bad_val,'%%'+cmd) +break} +cfmt[cmd]=f;img.chg=true +break +case"concert-score":if(cfmt.sound!="play") +cfmt.sound="concert" +break +case"writefields":set_writefields(param) +break +case"volume":cmd="dynamic" +case"dynamic":case"gchord":case"gstemdir":case"ornament":case"stemdir":case"vocal":set_pos(cmd,param) +break +case"font":get_font_scale(param) +break +case"fullsvg":if(parse.state!=0){syntax(1,errs.not_in_tune,"%%fullsvg") +break} +cfmt[cmd]=param +break +case"gracespace":v=param.split(/\s+/) +for(i=0;i<3;i++) +if(isNaN(+v[i])){syntax(1,errs.bad_val,"%%gracespace") +break} +if(parse.ufmt) +cfmt[cmd]=new Float32Array(3) +for(i=0;i<3;i++) +cfmt[cmd][i]=+v[i] +break +case"tuplets":v=param.split(/\s+/) +f=v[3] +if(f) +f=posval[f] +if(f) +v[3]=f +if(curvoice) +curvoice.tup=v +else +cfmt[cmd]=v +break +case"infoname":set_infoname(param) +break +case"notespacingfactor":v=param.match(/([.\d]+)[,\s]*(\d+)?/) +if(v){f=+v[1] +if(isNaN(f)||f<1||f>2){f=0}else if(v[2]){f2=+v[2] +if(isNaN(f)) +f=0}else{f2=cfmt.spatab[5]}} +if(!f){syntax(1,errs.bad_val,"%%"+cmd) +break} +cfmt[cmd]=param +cfmt.spatab=new Float32Array(10) +i=5;do{cfmt.spatab[i]=f2 +f2/=f}while(--i>=0) +i=5;f2=cfmt.spatab[i] +for(;++i1){syntax(1,errs.bad_val,'%%'+cmd) +break}} +cfmt[cmd]=v +break +case"combinevoices":syntax(1,"%%combinevoices is deprecated - use %%voicecombine instead") +break +case"voicemap":set_v_param("map",param) +break +case"voicescale":set_v_param("scale",param) +break +case"unsizedsvg":if(get_bool(param)) +user.imagesize="" +else +delete user.imagesize +break +case"rbdbstop":v=get_bool(param) +if(v&&cfmt["abc-version"]>="2.2") +cfmt["abc-version"]="1" +else if(!v&&cfmt["abc-version"]<"2.2") +cfmt["abc-version"]="2.2" +break +default:if(!parse.state) +cfmt[cmd]=param +break} +if(sfmt[cmd]&&parse.ufmt){parse.ufmt=false}} +function st_font(font){var n=font.name,r="" +if(font.weight) +r+=font.weight+" " +if(font.style) +r+=font.style+" " +if(n.indexOf('"')<0&&n.indexOf(' ')>0) +n='"'+n+'"' +return r+font.size.toFixed(1)+'px '+n} +function style_font(font){return'font:'+st_font(font)} +Abc.prototype.style_font=style_font +function font_class(font){var f='f'+font.fid+cfmt.fullsvg +if(font.class) +f+=' '+font.class +if(font.box) +f+=' '+'box' +return f} +function use_font(font){if(!font.used){font.used=true;if(font.fid==undefined){font.fid=abc2svg.font_tb.length +abc2svg.font_tb.push(font) +if(!font.swfac) +set_font_fac(font) +if(!font.pad) +font.pad=0 +font.cw_tb=!font.name?ssw_tb:font.name.indexOf("ans")>0?ssw_tb:font.name.indexOf("ono")>0?mw_tb:sw_tb} +add_fstyle(".f"+font.fid+ +(cfmt.fullsvg||"")+"{"+style_font(font)+"}") +if(font.src) +add_fstyle("@font-face{\n\ + font-family:"+font.name+";\n\ + src:"+font.src+"}") +if(font==cfmt.musicfont) +add_fstyle(".f"+font.fid ++(cfmt.fullsvg||"") ++' text,tspan{white-space:pre}')}} +function get_font(fn){var font,font2,fid,st +fn+="font" +font=cfmt[fn] +if(!font){syntax(1,"Unknown font $1",fn) +return gene.curfont} +if(!font.name||!font.size){font2=Object.create(gene.deffont) +if(font.name) +font2.name=font.name +if(font.normal){if(font2.weight) +font2.weight=null +if(font2.style) +font2.style=null}else{if(font.weight) +font2.weight=font.weight +if(font.style) +font2.style=font.style} +if(font.src) +font2.src=font.src +if(font.size) +font2.size=font.size +st=st_font(font2) +if(font.class){font2.class=font.class +st+=' '+font.class} +fid=abc2svg.font_st[st] +if(fid!=undefined) +return abc2svg.font_tb[fid] +abc2svg.font_st[st]=abc2svg.font_tb.length +font2.fid=font2.used=undefined +font=font2} +use_font(font) +return font} +var sav={},mac={},maci={},modone={} +var abc_utf={"=D":"Đ","=H":"Ħ","=T":"Ŧ","=d":"đ","=h":"ħ","=t":"ŧ","/O":"Ø","/o":"ø","/L":"Ł","/l":"ł","vL":"Ľ","vl":"ľ","vd":"ď",".i":"ı","AA":"Å","aa":"å","AE":"Æ","ae":"æ","DH":"Ð","dh":"ð","OE":"Œ","oe":"œ","ss":"ß","TH":"Þ","th":"þ"} +var oct_acc={"1":"\u266f","2":"\u266d","3":"\u266e","4":"𝄪","5":"𝄫"} +function cnv_escape(src,flag){var c,c2,dst="",i,j=0 +while(1){i=src.indexOf('\\',j) +if(i<0) +break +dst+=src.slice(j,i);c=src[++i] +if(!c) +return dst+'\\' +switch(c){case'0':case'2':if(src[i+1]!='0') +break +c2=oct_acc[src[i+2]] +if(c2){dst+=c2;j=i+3 +continue} +break +case'u':j=Number("0x"+src.slice(i+1,i+5));if(isNaN(j)||j<0x20){dst+=src[++i]+"\u0306" +j=i+1 +continue} +c=String.fromCharCode(j) +if(c=='\\'){i+=4 +break} +dst+=c +j=i+5 +continue +case't':dst+='\t';j=i+1 +continue +case'n':dst+='\n';j=i+1 +continue +default:c2=abc_utf[src.slice(i,i+2)] +if(c2){dst+=c2;j=i+2 +continue} +c2=src[i+1] +if(!c2) +break +if(!/[A-Za-z]/.test(c2)) +break +switch(c){case'`':dst+=c2+"\u0300" +j=i+2 +continue +case"'":dst+=c2+"\u0301" +j=i+2 +continue +case'^':dst+=c2+"\u0302" +j=i+2 +continue +case'~':dst+=c2+"\u0303" +j=i+2 +continue +case'=':dst+=c2+"\u0304" +j=i+2 +continue +case'_':dst+=c2+"\u0305" +j=i+2 +continue +case'.':dst+=c2+"\u0307" +j=i+2 +continue +case'"':dst+=c2+"\u0308" +j=i+2 +continue +case'o':dst+=c2+"\u030a" +j=i+2 +continue +case'H':dst+=c2+"\u030b" +j=i+2 +continue +case'v':dst+=c2+"\u030c" +j=i+2 +continue +case'c':dst+=c2+"\u0327" +j=i+2 +continue +case';':dst+=c2+"\u0328" +j=i+2 +continue} +break} +if(flag=='w') +dst+='\\' +dst+=c +j=i+1} +return dst+src.slice(j)} +var include=0 +function do_include(fn){var file,parse_sav +if(!user.read_file){syntax(1,"No read_file support") +return} +if(include>2){syntax(1,"Too many include levels") +return} +file=user.read_file(fn) +if(!file){syntax(1,"Cannot read file '$1'",fn) +return} +include++;parse_sav=clone(parse);tosvg(fn,file);parse_sav.state=parse.state;parse=parse_sav;include--} +function tosvg(in_fname,file,bol,eof){var i,c,eol,end,select,line0,line1,last_info,opt,text,a,b,s,pscom,txt_add='\n' +function tune_selected(){var re,res,i=file.indexOf('K:',bol) +if(i<0){return false} +i=file.indexOf('\n',i) +if(parse.select.test(file.slice(parse.bol,i))) +return true +re=/\n\w*\n/;re.lastIndex=i;res=re.exec(file) +if(res) +eol=re.lastIndex +else +eol=eof +return false} +function uncomment(src,flag){if(!src) +return src +var i=src.indexOf('%') +if(i==0) +return'' +if(i>0) +src=src.replace(/([^\\])%.*/,'$1').replace(/\\%/g,'%');src=src.replace(/\s+$/,'') +if(flag&&src.indexOf('\\')>=0) +return cnv_escape(src,flag) +return src} +function end_tune(){generate() +cfmt=sav.cfmt;info=sav.info;char_tb=sav.char_tb;glovar=sav.glovar;maps=sav.maps;mac=sav.mac;maci=sav.maci;parse.tune_v_opts=null;parse.scores=null;parse.ufmt=false +delete parse.ctrl +init_tune() +img.chg=true;set_page()} +function do_voice(select,in_tune){var opt,bol +if(select=="end") +return +if(in_tune){if(!parse.tune_v_opts) +parse.tune_v_opts={};opt=parse.tune_v_opts}else{if(!parse.voice_opts) +parse.voice_opts={};opt=parse.voice_opts} +opt[select]=[] +while(1){bol=++eol +if(file[bol]!='%') +break +eol=file.indexOf('\n',eol);if(file[bol+1]!=line1) +continue +bol+=2 +if(eol<0) +text=file.slice(bol) +else +text=file.slice(bol,eol);a=text.match(/\S+/) +switch(a[0]){default:opt[select].push(uncomment(text,true)) +continue +case"score":case"staves":case"tune":case"voice":bol-=2 +break} +break} +eol=parse.eol=bol-1} +function tune_filter(){var o,opts,j,pc,h,i=file.indexOf('K:',bol) +i=file.indexOf('\n',i);h=file.slice(parse.bol,i) +for(i in parse.tune_opts){if(!parse.tune_opts.hasOwnProperty(i)) +continue +if(!(new RegExp(i)).test(h)) +continue +opts=parse.tune_opts[i] +for(j=0;jeof) +eol=eof;parse.eol=eol +while(1){eol-- +switch(file[eol]){case' ':case'\t':continue} +break} +eol++ +if(eol==bol){if(parse.state==1){parse.istart=bol;syntax(1,"Empty line in tune header - ignored")}else if(parse.state>=2){end_tune() +parse.state=0 +if(parse.select){eol=file.indexOf('\nX:',parse.eol) +if(eol<0) +eol=eof +parse.eol=eol}} +continue} +parse.istart=parse.bol=bol;parse.iend=eol;parse.line.index=0;line0=file[bol];line1=file[bol+1] +if((line0=='I'&&line1==':')||line0=='%'){if(line0=='%'&&parse.prefix.indexOf(line1)<0) +continue +if(file[bol+2]=='a'&&file[bol+3]=='b'&&file[bol+4]=='c'&&file[bol+5]==' '){bol+=6;line0=file[bol];line1=file[bol+1]}else{pscom=true}} +if(pscom){pscom=false;bol+=2 +text=file.slice(bol,eol) +a=text.match(/([^\s]+)\s*(.*)/) +if(!a||a[1][0]=='%') +continue +switch(a[1]){case"abcm2ps":case"ss-pref":parse.prefix=a[2] +continue +case"abc-include":do_include(uncomment(a[2])) +continue} +if(a[1].slice(0,5)=='begin'){b=a[1].substr(5);end='\n'+line0+line1+"end"+b;i=file.indexOf(end,eol) +if(i<0){syntax(1,"No $1 after %%$2",end.slice(1),a[1]);parse.eol=eof +continue} +self.do_begin_end(b,uncomment(a[2]),file.slice(eol+1,i).replace(/\n%[^%].*$/gm,'').replace(/^%%/gm,'')) +parse.eol=file.indexOf('\n',i+6) +if(parse.eol<0) +parse.eol=eof +continue} +switch(a[1]){case"select":if(parse.state!=0){syntax(1,errs.not_in_tune,"%%select") +continue} +select=uncomment(text.slice(7)) +if(select[0]=='"') +select=select.slice(1,-1);if(!select){delete parse.select +continue} +select=select.replace(/\(/g,'\\(');select=select.replace(/\)/g,'\\)');parse.select=new RegExp(select,'m') +continue +case"tune":if(parse.state!=0){syntax(1,errs.not_in_tune,"%%tune") +continue} +select=uncomment(a[2]) +if(!select){parse.tune_opts={} +continue} +if(select=="end") +continue +if(!parse.tune_opts) +parse.tune_opts={};parse.tune_opts[select]=opt={t_opts:[]};while(1){bol=eol +if(file[bol+1]!='%') +break +eol=file.indexOf('\n',eol+1) +if(file[bol+2]!=line1) +continue +text=file.slice(bol+3,eol<0?undefined:eol) +a=text.match(/([^\s]+)\s*(.*)/) +switch(a[1]){case"tune":break +case"voice":do_voice(uncomment(a[2],true),true) +continue +default:opt.t_opts.push(uncomment(text,true)) +continue} +break} +if(parse.tune_v_opts){opt.v_opts=parse.tune_v_opts;parse.tune_v_opts=null} +parse.eol=bol +continue +case"voice":if(parse.state!=0){syntax(1,errs.not_in_tune,"%%voice") +continue} +select=uncomment(a[2]) +if(!select){parse.voice_opts=null +continue} +do_voice(select) +continue} +self.do_pscom(uncomment(text,true)) +continue} +if(line1!=':'||!/[A-Za-z+]/.test(line0)){last_info=undefined;if(parse.state<2) +continue +parse.line.buffer=uncomment(file.slice(bol,eol)) +if(parse.line.buffer) +parse_music_line() +continue} +bol+=2 +while(1){switch(file[bol]){case' ':case'\t':bol++ +continue} +break} +if(line0=='+'){if(!last_info){syntax(1,"+: without previous info field") +continue} +txt_add=' ';line0=last_info} +text=uncomment(file.slice(bol,eol),line0) +switch(line0){case'X':if(parse.state!=0){syntax(1,errs.ignored,line0) +continue} +if(parse.select&&!tune_selected()){eol=file.indexOf('\nX:',parse.eol) +if(eol<0) +eol=eof;parse.eol=eol +continue} +sav.cfmt=clone(cfmt);sav.info=clone(info,2) +sav.char_tb=clone(char_tb);sav.glovar=clone(glovar);sav.maps=clone(maps,1);sav.mac=clone(mac);sav.maci=clone(maci);info.X=text;parse.state=1 +if(user.page_format&&blkdiv<1) +blkdiv=1 +if(parse.tune_opts) +tune_filter() +continue +case'T':switch(parse.state){case 0:continue +case 1:case 2:if(info.T==undefined) +info.T=text +else +info.T+="\n"+text +continue} +s=new_block("title");s.text=text +continue +case'K':switch(parse.state){case 0:continue +case 1:info.K=text +break} +do_info(line0,text) +continue +case'W':if(parse.state==0||cfmt.writefields.indexOf(line0)<0) +break +if(info.W==undefined) +info.W=text +else +info.W+=txt_add+text +break +case'm':if(parse.state>=2){syntax(1,errs.ignored,line0) +continue} +a=text.match(/(.*?)[= ]+(.*)/) +if(!a||!a[2]){syntax(1,errs.bad_val,"m:") +continue} +mac[a[1]]=a[2];maci[a[1][0]]=true +break +case's':if(parse.state!=3||cfmt.writefields.indexOf(line0)<0) +break +get_sym(text,txt_add==' ') +break +case'w':if(parse.state!=3||cfmt.writefields.indexOf(line0)<0) +break +get_lyrics(text,txt_add==' ') +break +case'|':if(parse.state<2) +continue +parse.line.buffer=text +parse_music_line() +continue +default:if("ABCDFGHNOSZ".indexOf(line0)>=0){if(parse.state>=2){syntax(1,errs.ignored,line0) +continue} +if(!info[line0]) +info[line0]=text +else +info[line0]+=txt_add+text +break} +do_info(line0,text) +continue} +txt_add='\n';last_info=line0} +if(include) +return +if(parse.state==1){syntax(1,"End of file in tune header") +get_key("C")} +if(parse.state>=2) +end_tune();parse.state=0} +Abc.prototype.tosvg=tosvg +var gene,staff_tb,nstaff,tsnext,realwidth,insert_meter,spf_last,smallest_duration +var dx_tb=new Float32Array([10,10,11,13,15]) +var hw_tb=new Float32Array([4.7,5,6,7.2,7.5]) +var w_note=new Float32Array([3.5,3.7,5,6,7]) +function identify_note(s,dur_o){var head,flags,dots=0,dur=dur_o +if(dur%12!=0) +error(1,s,"Invalid note duration $1",dur);dur/=12 +if(!dur) +error(1,s,"Note too short") +for(flags=5;dur;dur>>=1,flags--){if(dur&1) +break} +dur>>=1 +if((dur+1)&dur){if(s.type!=C.REST||dur_o!=s.p_v.wmeasure) +error(0,s,"Non standard note duration $1",dur_o)} +while(dur>>dots>0) +dots++ +flags-=dots +if(flags>=0){head=C.FULL}else switch(flags){default:error(1,s,"Note too long") +flags=-4 +case-4:head=C.SQUARE +break +case-3:head=s.fmt.squarebreve?C.SQUARE:C.OVALBARS +break +case-2:head=C.OVAL +break +case-1:head=C.EMPTY +break} +return[head,dots,flags]} +function set_head_shift(s){var i,i1,i2,d,ps,dx,dx_head=dx_tb[s.head],dir=s.stem,n=s.nhd +if(!n) +return +dx=dx_head*.74 +if(s.grace) +dx*=.6 +if(dir>=0){i1=1;i2=n+1;ps=s.notes[0].pit}else{dx=-dx;i1=n-1;i2=-1;ps=s.notes[n].pit} +var shift=false,dx_max=0 +for(i=i1;i!=i2;i+=dir){d=s.notes[i].pit-ps;ps=s.notes[i].pit +if(!d){if(shift){var new_dx=s.notes[i].shhd=s.notes[i-dir].shhd+dx +if(dx_max3||(d>=2&&s.head!=C.SQUARE)){shift=false}else{shift=!shift +if(shift){s.notes[i].shhd=dx +if(dx_max=0;){dx=notes[i].shhd +if(!dx||dx>0) +continue +dx=dx_head-dx;ps=notes[i].pit +for(i1=n;--i1>=0;){if(!notes[i1].acc) +continue +p1=notes[i1].pit +if(p1ps+3) +continue +if(notes[i1].shac=0;){if(notes[i1].acc){p1=notes[i1].pit +dx1=notes[i1].shac +if(!dx1){dx1=notes[i1].shhd +if(dx1<0) +dx1=dx_head-dx1 +else +dx1=dx_head} +break}} +if(i1<0) +return +for(i2=0;i2ps+4){if(dx1>dx2) +dx2=dx1 +notes[i1].shac=notes[i2].shac=dx2}else{notes[i1].shac=dx1 +if(notes[i1].pit!=notes[i2].pit) +dx1+=7 +notes[i2].shac=dx2=dx1} +dx2+=7 +for(i=i1;--i>i2;){acc=notes[i].acc +if(!acc) +continue +dx=notes[i].shac +if(dxi;){if(!notes[i1].acc) +continue +p1=notes[i1].pit +if(p1>=ps+4){if(p1>ps+4||acc<0||notes[i1].acc<0) +continue} +if(dx>notes[i1].shac-6){dx1=notes[i1].shac+7 +if(dx1>dx) +dx=dx1}} +notes[i].shac=dx}} +function set_acc_shft(){var s,s2,st,i,acc,st,t,dx_head,notes +s=tsfirst +while(s){if(s.type!=C.NOTE||s.invis){s=s.ts_next +continue} +st=s.st;t=s.time;acc=false +for(s2=s;s2;s2=s2.ts_next){if(s2.time!=t||s2.type!=C.NOTE||s2.st!=st) +break +if(acc) +continue +for(i=0;i<=s2.nhd;i++){if(s2.notes[i].acc){acc=true +break}}} +if(!acc){s=s2 +continue} +dx_head=dx_tb[s.head] +notes=[] +for(;s!=s2;s=s.ts_next){if(!s.invis) +Array.prototype.push.apply(notes,s.notes)} +notes.sort(abc2svg.pitcmp) +acc_shift(notes,dx_head)}} +function lkvsym(s,next){s.next=next;s.prev=next.prev +if(s.prev) +s.prev.next=s +else +s.p_v.sym=s;next.prev=s} +function lktsym(s,next){var old_wl +s.ts_next=next +if(next){s.ts_prev=next.ts_prev +if(s.ts_prev) +s.ts_prev.ts_next=s;next.ts_prev=s}else{error(2,s,"Bad linkage") +s.ts_prev=null} +s.seqst=!s.ts_prev||s.time!=s.ts_prev.time||(w_tb[s.ts_prev.type]!=w_tb[s.type]&&!!w_tb[s.ts_prev.type]) +if(!next||next.seqst) +return +next.seqst=next.time!=s.time||(w_tb[s.type]!=w_tb[next.type]&&!!w_tb[s.type]) +if(next.seqst){old_wl=next.wl +self.set_width(next) +if(next.a_ly) +ly_set(next) +if(!next.shrink){next.shrink=next.wl +if(next.prev) +next.shrink+=next.prev.wr}else{next.shrink+=next.wl-old_wl} +next.space=0}} +function unlksym(s){if(s.next) +s.next.prev=s.prev +if(s.prev) +s.prev.next=s.next +else +s.p_v.sym=s.next +if(s.ts_next){if(s.seqst){if(s.ts_next.seqst){s.ts_next.shrink+=s.shrink;s.ts_next.space+=s.space}else{s.ts_next.seqst=true;s.ts_next.shrink=s.shrink;s.ts_next.space=s.space}}else{if(s.ts_next.seqst&&s.ts_prev&&s.ts_prev.seqst&&!w_tb[s.ts_prev.type]){s.ts_next.seqst=false +s.shrink=s.ts_next.shrink +s.space=s.ts_next.space}} +s.ts_next.ts_prev=s.ts_prev} +if(s.ts_prev) +s.ts_prev.ts_next=s.ts_next +if(tsfirst==s) +tsfirst=s.ts_next +if(tsnext==s) +tsnext=s.ts_next} +function insert_clef(s,clef_type,clef_line){var p_voice=s.p_v,new_s,st=s.st +if(s.type==C.BAR&&s.prev&&s.prev.type==C.BAR&&s.prev.bar_type[0]!=':') +s=s.prev;p_voice.last_sym=s.prev +if(!p_voice.last_sym) +p_voice.sym=null;p_voice.time=s.time;new_s=sym_add(p_voice,C.CLEF);new_s.next=s;s.prev=new_s;new_s.clef_type=clef_type;new_s.clef_line=clef_line;new_s.st=st;new_s.clef_small=true +delete new_s.second;new_s.notes=[] +new_s.notes[0]={pit:s.notes[0].pit} +new_s.nhd=0;while(!s.seqst) +s=s.ts_prev;lktsym(new_s,s) +if(s.soln){new_s.soln=true +delete s.soln} +return new_s} +function set_float(){var p_voice,st,staff_chg,v,s,s1,up,down +for(v=0;v=19){staff_chg=false +continue} +if(s.notes[s.nhd].pit<=12){staff_chg=true +s.st++ +continue} +up=127 +for(s1=s.ts_prev;s1;s1=s1.ts_prev){if(s1.st!=st||s1.v==s.v) +break +if(s1.type==C.NOTE) +if(s1.notes[0].pitup-3){staff_chg=false +continue} +down=-127 +for(s1=s.ts_next;s1;s1=s1.ts_next){if(s1.st!=st+1||s1.v==s.v) +break +if(s1.type==C.NOTE) +if(s1.notes[s1.nhd].pit>down) +down=s1.notes[s1.nhd].pit} +if(down==-127){if(staff_chg) +s.st++ +continue} +if(s.notes[0].pit=0;m--){if(g.notes[m].shac-2>dx) +dx=g.notes[m].shac-2} +x+=dx;g.x=x +if(g.nflags<=0) +g.beam_st=g.beam_end=true +next=g.next +if(!next){g.beam_end=true +break} +if(next.nflags<=0) +g.beam_end=true +if(g.beam_end){next.beam_st=true;x+=gspinside/4} +if(g.nflags<=0) +x+=gspinside/4 +if(g.y>next.y+8) +x-=1.5 +x+=gspinside} +next=s.next +if(next&&next.type==C.NOTE){if(g.y>=3*(next.notes[next.nhd].pit-18)) +gspright-=1 +else if(g.beam_st&&g.y<3*(next.notes[next.nhd].pit-18)-4) +gspright+=2} +x+=gspright;return x} +function set_w_chs(s){var i,ch,w0,s0,dw,x=0,n=0 +set_font("vocal") +for(;s;s=s.ts_next){if(s.shrink){x+=s.shrink;n++} +if(s.a_ly) +ly_set(s) +if(!s.a_gch) +continue +for(i=0;ix+ch.x){if(s.prev&&s.prev.seqst&&s.prev.type==C.BAR) +n-- +dw=(w0-x-ch.x)/n +while(1){s0=s0.ts_next +if(s0.shrink) +s0.shrink+=dw +if(s0==s||s0.type==C.BAR) +break}}} +s0=s;w0=ch.text.wh[0];n=0;x=0 +break}}} +function gchord_width(s,wlnote,wlw){var gch,w,ix,arspc=0 +for(ix=0;ixwlw) +wlw=w +break +case'>':w=gch.text.wh[0]+s.wr +if(w>arspc) +arspc=w +break}} +if(s.wr0) +s.wr+=s.xmx+4;for(s2=s.prev;s2;s2=s2.prev){if(w_tb[s2.type]) +break} +if(s2){switch(s2.type){case C.BAR:case C.CLEF:case C.KEY:case C.METER:wlnote+=3 +break +case C.STBRK:wlnote+=8 +break}} +for(m=0;m<=s.nhd;m++){nt=s.notes[m] +xx=nt.shhd +if(xx<0){if(wlnote<-xx+5) +wlnote=-xx+5} +acc=nt.acc +if(acc){tmp=nt.shac+ +(typeof acc=="object"?5.5:3.5) +if(wlnote0&&s.nflags>0){if(s.wr=2) +s.wr+=3.5*(s.dots-1)} +if(s.trem2&&s.beam_end&&wlnote<20) +wlnote=20 +wlw=wlnote +if(s2){switch(s2.type){case C.NOTE:if(s2.stem>0&&s.stem<0){if(wlw<7) +wlw=7} +if((s.y>27&&s2.y>27)||(s.y<-3&&s2.y<-3)){if(wlw<6) +wlw=6} +if(s2.tie){if(wlw<14) +wlw=14} +break +case C.CLEF:if(s2.second||s2.clef_small) +break +case C.KEY:if(s.a_gch) +wlw+=4 +case C.METER:wlw+=3 +break}} +if(s.a_gch) +wlw=gchord_width(s,wlnote,wlw) +if(s.prev&&s.prev.type==C.GRACE){s.prev.wl+=wlnote-4.5 +s.wl=s.prev.wl}else{s.wl=wlw} +return +case C.SPACE:xx=s.width/2;s.wr=xx +if(s.a_gch) +xx=gchord_width(s,xx,xx) +if(s.a_dd) +xx=deco_width(s,xx) +s.wl=xx +return +case C.BAR:bar_type=s.bar_type +switch(bar_type){case"|":w=5 +break +case"[":w=0 +break +default:w=2+2.8*bar_type.length +for(i=0;i0&s.bar_num&&s.bar_num%cfmt.measurenb) +s.wr+=4} +return +case C.CLEF:if(s.invis){s.wl=s.wr=1 +return} +if(s.prev&&s.prev.type==C.STBRK){s.wl=6 +s.wr=13 +delete s.clef_small +return} +s.wl=s.clef_small?11:12 +s.wr=s.clef_small?8:13 +return +case C.KEY:if(s.invis){s.wl=s.wr=0 +return} +s.wl=0 +esp=3 +n1=s.k_sf +if(s.k_old_sf&&(s.fmt.cancelkey||n1==0)) +n2=s.k_old_sf +else +n2=0 +if(n1*n2>=0){if(n1<0) +n1=-n1 +if(n2<0) +n2=-n2 +if(n2>n1) +n1=n2}else{n1-=n2 +if(n1<0) +n1=-n1;esp+=3} +if(s.k_bagpipe=='p') +n1++ +if(s.k_a_acc){n2=s.k_a_acc.length +if(s.exp) +n1=n2 +else +n1+=n2 +if(n2) +last_acc=s.k_a_acc[0].acc +for(i=1;is.k_a_acc[i-1].pit+6||acc.pitmeter.bot.length) +meter=meter.top +else +meter=meter.bot;for(m=0;m=C.BLEN/2){if(smallest_duration>=C.BLEN) +len/=4 +else +len/=2}else if(!s.next&&len>=C.BLEN){len/=2} +if(len>=C.BLEN/4){if(len=C.BLEN/8) +i=4 +else if(len>=C.BLEN/16) +i=3 +else if(len>=C.BLEN/32) +i=2 +else if(len>=C.BLEN/64) +i=1 +else +i=0} +l=len-((C.BLEN/16/8)<=9) +i=8 +space+=(cfmt.spatab[i+1]-cfmt.spatab[i])*l/((C.BLEN/16/8)<=-1&&s.stem>0){stemdir=true +for(s2=s.ts_prev;s2&&s2.time==ptime;s2=s2.ts_prev){if(s2.type==C.NOTE&&(s2.nflags<-1||s2.stem>0)){stemdir=false +break}} +if(stemdir){for(s2=s.ts_next;s2&&s2.time==s.time;s2=s2.ts_next){if(s2.type==C.NOTE&&(s2.nflags<-1||s2.stem<0)){stemdir=false +break}} +if(stemdir) +space*=.9}} +return space} +function set_sp_tup(s,s_et){var tim=s.time,ttim=s_et.time-tim,sp=time2space(s,ttim),s2=s,wsp=0 +while(1){s2=s2.ts_next +if(s2.seqst){wsp+=s2.space +if(s2.bar_type) +wsp+=10} +if(s2==s_et) +break} +sp=(sp+wsp)/2/ttim +while(1){s=s.ts_next +if(s.seqst){s.space=sp*(s.time-tim) +tim=s.time} +if(s==s_et) +break}} +function _bar(s){return{type:C.BAR,bar_type:"|",fname:s.fname,istart:s.istart,iend:s.iend,v:s.v,p_v:s.p_v,st:s.st,dur:0,time:s.time+(s.dur||0),nhd:0,notes:[{pit:s.notes?s.notes[0].pit:22}],seqst:true,invis:true,prev:s,fmt:s.fmt}} +function add_end_bar(s){var b=_bar(s),sn=s.ts_next +b.wl=0 +b.wr=0 +b.ts_prev=s +b.next=s.next +b.ts_next=s.ts_next +b.shrink=s.type==C.STBRK?0:(s.wr+3) +if(s.next) +s.next.prev=b +s.ts_next.ts_prev=b +s.next=s.ts_next=b +b.space=sn.space*.9-3 +return b} +function set_allsymwidth(first){var val,st,s_chs,stup,itup,s=tsfirst,s2=s,xa=0,xl=[],wr=[],maxx=xa,tim=s.time +while(1){itup=0 +do{if((s.a_gch||s.a_ly)&&!s_chs) +s_chs=s;self.set_width(s);st=s.st +if(xl[st]==undefined) +xl[st]=0 +if(wr[st]==undefined) +wr[st]=0;val=xl[st]+wr[st]+s.wl +if(val>maxx) +maxx=val +if(s.dur&&s.dur!=s.notes[0].dur&&first) +itup=1 +s=s.ts_next}while(s&&!s.seqst);s2.shrink=maxx-xa +s2.space=s2.ts_prev?set_space(s2,tim):0 +if(s2.space==0&&s2.ts_prev&&s2.ts_prev.type==C.SPACE&&s2.ts_prev.seqst) +s2.space=s2.ts_prev.space/=2 +if(itup){if(!stup) +stup=s2}else if(stup&&stup.v==s2.v){set_sp_tup(stup,s2) +stup=null} +if(!s2.shrink){if(s2.type==C.CLEF&&!s2.ts_prev.bar_type){delete s2.seqst;s2.time=tim}else{s2.shrink=10}} +tim=s2.time +if(!s) +break +s=s2 +do{wr[s.st]=0 +s=s.ts_next}while(!s.seqst) +xa=maxx +do{st=s2.st;xl[st]=xa +if(s2.wr>wr[st]) +wr[st]=s2.wr +s2=s2.ts_next}while(!s2.seqst)} +if(stup) +set_sp_tup(stup,s2) +if(first&&s_chs) +set_w_chs(s_chs)} +function to_rest(so){var s=clone(so) +s.prev.next=so.ts_prev=so.prev=s.ts_prev.ts_next=s +s.next=s.ts_next=so +so.seqst=false +so.invis=so.play=true +s.type=C.REST +delete s.in_tuplet +delete s.tp +delete s.a_dd +delete s.a_gch +delete s.sls +return s} +function set_repeat(s){var s2,s3,i,j,dur,n=s.repeat_n,k=s.repeat_k,st=s.st,v=s.v +s.repeat_n=0 +if(n<0){n=-n;i=n +for(s3=s.prev;s3;s3=s3.prev){if(!s3.dur){if(s3.type==C.BAR){error(1,s3,"Bar in repeat sequence") +return} +continue} +if(--i<=0) +break} +if(!s3){error(1,s,errs.not_enough_n) +return} +dur=s.time-s3.time;i=k*n +for(s2=s;s2;s2=s2.next){if(!s2.dur){if(s2.type==C.BAR){error(1,s2,"Bar in repeat sequence") +return} +continue} +if(--i<=0) +break} +if(!s2||!s2.next){error(1,s,errs.not_enough_n) +return} +for(s2=s.prev;s2!=s3;s2=s2.prev){if(s2.type==C.NOTE){s2.beam_end=true +break}} +for(j=k;--j>=0;){i=n +if(s.dur) +i--;s2=s.ts_next +while(i>0){if(s2.st==st){s2.invis=s2.play=true +if(s2.seqst&&s2.ts_next.seqst) +s2.seqst=false +if(s2.v==v&&s2.dur) +i--} +s2=s2.ts_next} +s=to_rest(s) +s.dur=s.notes[0].dur=dur;s.rep_nb=-1;s.beam_st=true;self.set_width(s) +s.head=C.SQUARE;for(s=s2;s;s=s.ts_next){if(s.st==st&&s.v==v&&s.dur) +break}} +return} +i=n +for(s2=s.prev.prev;s2;s2=s2.prev){if(s2.type==C.BAR||s2.time==tsfirst.time){if(--i<=0) +break}} +if(!s2){error(1,s,errs.not_enough_m) +return} +dur=s.time-s2.time +if(n==1) +i=k +else +i=n +for(s2=s;s2;s2=s2.next){if(s2.type==C.BAR){if(--i<=0) +break}} +if(!s2){error(1,s,errs.not_enough_m) +return} +i=k +if(n==2&&i>1){s2=s2.next +if(!s2){error(1,s,errs.not_enough_m) +return} +s2.repeat_n=n;s2.repeat_k=--i} +dur/=n +if(n==2){s3=s +for(s2=s.ts_next;;s2=s2.ts_next){if(s2.st!=st) +continue +if(s2.type==C.BAR){if(s2.v==v) +break +continue} +s2.invis=s2.play=true +if(s2.seqst&&s2.ts_next.seqst) +s2.seqst=false} +s3=to_rest(s3) +s3.dur=s3.notes[0].dur=dur;s3.invis=true +s2.bar_mrep=2 +s3=s2.next;for(s2=s3.ts_next;;s2=s2.ts_next){if(s2.st!=st) +continue +if(s2.type==C.BAR){if(s2.v==v) +break +continue} +if(!s2.dur) +continue +s2.invis=s2.play=true +if(s2.seqst&&s2.ts_next.seqst) +s2.seqst=false} +s3=to_rest(s3) +s3.dur=s3.notes[0].dur=dur;s3.invis=true;self.set_width(s3) +return} +s3=s +for(j=k;--j>=0;){for(s2=s3.ts_next;;s2=s2.ts_next){if(s2.st!=st) +continue +if(s2.type==C.BAR){if(s2.v==v) +break +continue} +if(!s2.dur) +continue +s2.invis=s2.play=true +if(s2.seqst&&s2.ts_next.seqst) +s2.seqst=false} +s3=to_rest(s3) +s3.dur=s3.notes[0].dur=dur;s3.beam_st=true +if(k==1){s3.rep_nb=1 +break} +s3.rep_nb=k-j+1;s3=s2.next}} +function custos_add(s){var p_voice,new_s,i,s2=s +while(1){if(s2.type==C.NOTE) +break +s2=s2.next +if(!s2) +return} +p_voice=s.p_v;p_voice.last_sym=s.prev;p_voice.time=s.time;new_s=sym_add(p_voice,C.CUSTOS);new_s.next=s;s.prev=new_s;new_s.wl=0 +new_s.wr=4 +lktsym(new_s,s);new_s.shrink=s.shrink +if(new_s.shrink<8+4) +new_s.shrink=8+4;new_s.space=s2.space;new_s.head=C.FULL +new_s.stem=s2.stem +new_s.nhd=s2.nhd;new_s.notes=[] +for(i=0;iw) +w=s4.wr +if(s4.seqst) +break +s4=s4.ts_prev} +s3.shrink+=w +s3.space=0 +s4=s3 +while(1){if(s4.ts_next.seqst) +break +s4=s4.ts_next} +w=0 +while(1){if(s4.wl>w) +w=s4.wl +s4=s4.ts_next +if(s4.seqst) +break} +s4.shrink=s3.wr+w} +delete s3.part +continue} +if(w_tb[s2.type]) +break}} +s=bardiv(s) +do_warn(s) +if(s.ts_prev.type!=C.STAVES){set_eol(s) +return s} +for(s=s.ts_prev;s;s=s.ts_prev){if(s.seqst&&s.type!=C.CLEF) +break} +done=0 +ptyp=s.type +for(;;s=s.ts_next){if(!s) +return s +if(s.type==ptyp) +continue +ptyp=s.type +if(done<0) +break +switch(s.type){case C.STAVES:if(!s.ts_prev) +return +if(s.ts_prev.type==C.BAR) +break +while(s.ts_next){if(w_tb[s.ts_next.type]&&s.ts_next.type!=C.CLEF) +break +s=s.ts_next} +if(!s.ts_next||s.ts_next.type!=C.BAR) +continue +s=s.ts_next +case C.BAR:if(done) +break +done=1;continue +case C.STBRK:if(!s.stbrk_forced) +unlksym(s) +else +done=-1 +continue +case C.CLEF:if(done) +break +continue +default:if(!done||(s.prev&&s.prev.type==C.GRACE)) +continue +break} +break} +set_eol(s) +return s} +function get_ck_width(){var r0,r1,p_voice=voice_tb[0] +self.set_width(p_voice.clef);self.set_width(p_voice.ckey);self.set_width(p_voice.meter) +return[p_voice.clef.wl+p_voice.clef.wr+ +p_voice.ckey.wl+p_voice.ckey.wr,p_voice.meter.wl+p_voice.meter.wr]} +function get_width(s,next){var shrink,space,w=0,wmx=0,sp_fac=(1-s.fmt.maxshrink) +while(s!=next){if(s.seqst){shrink=s.shrink +wmx+=shrink +if((space=s.space)=xmin) +break} +s4=s +if(s==next){if(s) +s=set_nl(s) +return s} +s3=null +for(;s!=next;s=s.ts_next){x=s.x +if(!x) +continue +if(x>xmax) +break +if(s.type!=C.BAR) +continue +if(x=xmax) +break +if(!beam&&!s.in_tuplet&&(xmid-s5.x>x-xmid||(s.time-bar_time)%(C.BLEN/4)==0)) +s3=s} +if(s.beam_st) +beam|=1<=xmax) +break +if(s3&&x>=xmid){if(xmid-s3.x>x-xmid) +s3=s +break} +s3=s}} +s=s3 +while(s.ts_next){s=s.ts_next +if(s.seqst) +break} +if(s.nl){error(0,s,"Line split problem - adjust maxshrink and/or breaklimit");nlines=2 +for(s=s.ts_next;s!=next;s=s.ts_next){if(!s.x) +continue +if(--nlines<=0) +break}} +s=set_nl(s) +if(!s||(next&&s.time>=next.time)) +break +wwidth-=s.x-first.x;indent=0} +return s} +function cut_tune(lwidth,lsh){var s2,i,mc,pg_sav={leftmargin:cfmt.leftmargin,rightmargin:cfmt.rightmargin,pagewidth:cfmt.pagewidth,scale:cfmt.scale},indent=lsh[0]-lsh[1],ckw=get_ck_width(),s=tsfirst +lwidth-=lsh[1] +if(cfmt.indent&&cfmt.indent>lsh[0]) +indent+=cfmt.indent +lwidth-=ckw[0] +indent+=ckw[1] +if(cfmt.custos&&voice_tb.length==1) +lwidth-=12 +i=s.fmt.barsperstaff +if(i){for(s2=s;s2;s2=s2.ts_next){if(s2.type!=C.BAR||!s2.bar_num||--i>0) +continue +while(s2.ts_next&&s2.ts_next.type==C.BAR) +s2=s2.ts_next +if(s2.ts_next) +s2.ts_next.soln=true +i=s.fmt.barsperstaff}} +s2=s +for(;s;s=s.ts_next){if(s.type==C.BLOCK){switch(s.subtype){case"leftmargin":case"rightmargin":case"pagescale":case"pagewidth":case"scale":case"staffwidth":if(!s.soln) +self.set_format(s.subtype,s.param) +break +case"mc_start":mc={lm:cfmt.leftmargin,rm:cfmt.rightmargin} +break +case"mc_new":case"mc_end":if(!mc) +break +cfmt.leftmargin=mc.lm +cfmt.rightmargin=mc.rm +img.chg=1 +break}} +if(!s.ts_next){s=null}else if(!s.soln){continue}else{s.soln=false +if(s.time==s2.time) +continue +while(!s.seqst) +s=s.ts_prev} +set_page() +lwidth=get_lwidth()-lsh[1]-ckw[0] +s2=set_lines(s2,s,lwidth,indent) +if(!s2) +break +s=s2.type==C.BLOCK?s2.ts_prev:s +indent=0} +cfmt.leftmargin=pg_sav.leftmargin +cfmt.rightmargin=pg_sav.rightmargin +cfmt.pagewidth=pg_sav.pagewidth +cfmt.scale=pg_sav.scale +img.chg=1 +set_page()} +function set_yval(s){switch(s.type){case C.CLEF:if(s.second||s.invis){s.ymx=s.ymn=12 +break} +s.y=(s.clef_line-1)*6 +switch(s.clef_type){default:s.ymx=s.y+28 +s.ymn=s.y-14 +break +case"c":s.ymx=s.y+13 +s.ymn=s.y-11 +break +case"b":s.ymx=s.y+7 +s.ymn=s.y-12 +break} +if(s.clef_small){s.ymx-=2;s.ymn+=2} +if(s.ymx<26) +s.ymx=26 +if(s.ymn>-1) +s.ymn=-1 +if(s.clef_octave){if(s.clef_octave>0) +s.ymx+=12 +else +s.ymn-=12} +break +case C.KEY:if(s.k_sf>2) +s.ymx=24+10 +else if(s.k_sf>0) +s.ymx=24+6 +else +s.ymx=24+2;s.ymn=-2 +break +default:s.ymx=24;s.ymn=0 +break}} +function set_ottava(){var s,s1,st,o,d,m=nstaff+1,staff_d=new Int8Array(m) +function sym_ott(s,d){var g,m,note +switch(s.type){case C.REST:if(voice_tb.length==1) +break +case C.NOTE:if(!s.p_v.ckey.k_drum){for(m=s.nhd;m>=0;m--){note=s.notes[m];if(!note.opit) +note.opit=note.pit;note.pit+=d}} +break +case C.GRACE:for(g=s.extra;g;g=g.next){if(!s.p_v.ckey.k_drum){for(m=0;m<=g.nhd;m++){note=g.notes[m] +if(!note.opit) +note.opit=note.pit +note.pit+=d}}} +break}} +function deco_rm(s){for(var i=s.a_dd.length;--i>=0;){if(s.a_dd[i].name.match(/1?[85][vm][ab]/)) +s.a_dd.splice(i,1)}} +for(s=tsfirst;s;s=s.ts_next){st=s.st +o=s.ottava +if(o){if(o[0]){if(staff_d[st]&&!o[1]){sym_ott(s,staff_d[st]) +deco_rm(s) +continue}}else if(!staff_d[st]){deco_rm(s) +continue} +s1=s +while(s1&&!s1.seqst) +s1=s1.ts_prev +if(s1){while(s1!=s){if(s1.st==st){if(o[1]) +sym_ott(s1,-staff_d[st]) +if(o[0]) +sym_ott(s1,-o[0]*7)} +s1=s1.ts_next}} +if(o[0]){staff_d[st]=-o[0]*7}else{staff_d[st]=0}} +if(staff_d[st]) +sym_ott(s,staff_d[st])}} +function mrest_expand(){var s,s2 +function mexp(s){var bar,s3,s4,tim,nbar,nb=s.nmes,dur=s.dur/nb,s2=s.next +while(!s2.bar_type) +s2=s2.next +bar=s2 +while(!s2.bar_num) +s2=s2.ts_prev +nbar=s2.bar_num-s.nmes +s.type=C.REST +s.notes[0].dur=s.dur=s.dur_orig=dur +s.nflags=-2 +s.head=C.FULL +s.fmr=1 +tim=s.time+dur +s3=s +while(--nb>0){s2=clone(bar) +delete s2.soln +delete s2.a_gch +delete s2.a_dd +delete s2.text +delete s2.rbstart +delete s2.rbstop +lkvsym(s2,s.next) +s2.time=tim +while(s3.timemax) +max=s.notes[s.nhd].pit} +if(min>=19||(min>=13&&clef_type_start!='b')) +return't' +if(max<=13||(max<=19&&clef_type_start!='t')) +return'b' +if(clef_type_start=='a'){if((max+min)/2>=16) +clef_type_start='t' +else +clef_type_start='b'} +var clef_type=clef_type_start,s_last=s,s_last_chg=null +for(s=s_start;s!=s_last;s=s.ts_next){if(s.type==C.STAVES&&s!=s_start) +break +if(s.st!=st||s.type!=C.NOTE) +continue +time=s.time +if(clef_type=='t'){if(s.notes[0].pit>12||s.notes[s.nhd].pit>20){if(s.notes[0].pit>20) +s_last_chg=s +continue} +s2=s.ts_prev +if(s2&&s2.time==time&&s2.st==st&&s2.type==C.NOTE&&s2.notes[0].pit>=19) +continue +s2=s.ts_next +if(s2&&s2.st==st&&s2.time==time&&s2.type==C.NOTE&&s2.notes[0].pit>=19) +continue}else{if(s.notes[0].pit<=12||s.notes[s.nhd].pit<20){if(s.notes[s.nhd].pit<=12) +s_last_chg=s +continue} +s2=s.ts_prev +if(s2&&s2.time==time&&s2.st==st&&s2.type==C.NOTE&&s2.notes[0].pit<=13) +continue +s2=s.ts_next +if(s2&&s2.st==st&&s2.time==time&&s2.type==C.NOTE&&s2.notes[0].pit<=13) +continue} +if(!s_last_chg){clef_type=clef_type_start=clef_type=='t'?'b':'t';s_last_chg=s +continue} +s3=s +for(s2=s.ts_prev;s2!=s_last_chg;s2=s2.ts_prev){if(s2.st!=st) +continue +if(s2.type==C.BAR){s3=s2.bar_type[0]!=':'?s2:s2.next +break} +if(s2.type!=C.NOTE) +continue +if(s2.beam_st&&!s2.p_v.second) +s3=s2} +if(s3.time==s_last_chg.time){s_last_chg=s +continue} +s_last_chg=s;clef_type=clef_type=='t'?'b':'t';s2=insert_clef(s3,clef_type,clef_type=="t"?2:4);s2.clef_auto=true} +return clef_type_start} +function set_clefs(){var s,s2,st,v,p_voice,g,new_type,new_line,p_staff,pit,staff_clef=new Array(nstaff+1),sy=cur_sy,mid=[] +staff_tb=new Array(nstaff+1) +for(st=0;st<=nstaff;st++){staff_clef[st]={autoclef:true} +staff_tb[st]={output:"",sc_out:""}} +for(st=0;st<=sy.nstaff;st++) +mid[st]=(sy.staves[st].stafflines.length-1)*3 +for(s=tsfirst;s;s=s.ts_next){if(s.repeat_n) +set_repeat(s) +switch(s.type){case C.STAVES:sy=s.sy +for(st=0;st<=nstaff;st++) +staff_clef[st].autoclef=true +for(v=0;v1||s.bar_mrep){s.y=12 +s.ymx=38 +s.ymn=0 +break} +if(voice_tb.length==1){s.y=12;s.ymx=24;s.ymn=0 +break} +case C.NOTE:delta=staff_delta[st] +if(delta&&!s.p_v.ckey.k_drum){for(m=s.nhd;m>=0;m--){note=s.notes[m] +note.opit=note.pit +note.pit+=delta}} +if(s.type==C.REST){s.y=(((s.notes[0].pit-18)/2)|0)*6;s.ymx=s.y+rest_sp[5-s.nflags][0];s.ymn=s.y-rest_sp[5-s.nflags][1]} +if(s.durnst){var msg="*** fatal set_stem_dir(): bad staff number "+st+" max "+nst;error(2,null,msg);throw new Error(msg)} +v=u.v;v_st=v_st_tb[v] +if(!v_st){v_st={st1:-1,st2:-1} +v_st_tb[v]=v_st} +if(v_st.st1<0){v_st.st1=st}else if(v_st.st1!=st){if(st>v_st.st1){if(st>v_st.st2) +v_st.st2=st}else{if(v_st.st1>v_st.st2) +v_st.st2=v_st.st1;v_st.st1=st}} +st_v=st_v_tb[st];rvoice=sy.voices[v].range;for(i=st_v.length;--i>=0;){vobj=st_v[i] +if(vobj.v==rvoice) +break} +if(i<0){vobj={v:rvoice,ymx:0,ymn:24} +for(i=0;ivobj.ymx) +vobj.ymx=u.ymx +if(u.ymn=0){if(st==v_st.st1) +s.multi=-1 +else if(st==v_st.st2) +s.multi=1 +continue} +if(st_v.length<=1){if(s.floating) +s.multi=st==voice_tb[v].st?-1:1 +continue} +rvoice=sy.voices[v].range +for(i=st_v.length;--i>=0;){if(st_v[i].v==rvoice) +break} +if(i<0) +continue +if(i==st_v.length-1){s.multi=-1}else{s.multi=1 +if(i&&i+2==st_v.length){if(st_v[i].ymn-s.fmt.stemheight>=st_v[i+1].ymx) +s.multi=-1;t=s.ts_next +if(s.ts_prev&&s.ts_prev.time==s.time&&s.ts_prev.st==s.st&&s.notes[s.nhd].pit==s.ts_prev.notes[0].pit&&s.beam_st&&s.beam_end&&(!t||t.st!=s.st||t.time!=s.time)) +s.multi=-1}}} +while(s&&s.type==C.BAR) +s=s.ts_next}} +function set_rest_offset(){var s,s2,v,end_time,not_alone,v_s,y,ymax,ymin,shift,dots,dx,v_s_tb=[],sy=cur_sy +for(s=tsfirst;s;s=s.ts_next){if(s.invis) +continue +if(s.type==C.STAVES) +sy=s.sy +if(!s.dur) +continue +v_s=v_s_tb[s.v] +if(!v_s){v_s={} +v_s_tb[s.v]=v_s} +v_s.s=s;v_s.st=s.st;v_s.end_time=s.time+s.dur +if(s.type!=C.REST) +continue +ymin=-127;ymax=127;not_alone=dots=false +for(v=0;v<=v_s_tb.length;v++){v_s=v_s_tb[v] +if(!v_s||!v_s.s||v_s.st!=s.st||v==s.v) +continue +if(v_s.end_time<=s.time) +continue +not_alone=true;s2=v_s.s +if(sy.voices[v].rangeymin){ymin=s2.ymx +if(s2.dots) +dots=true}}else{if(s2.y>ymin) +ymin=s2.y}}} +end_time=s.time+s.dur +for(s2=s.ts_next;s2;s2=s2.ts_next){if(s2.time>=end_time) +break +if(s2.st!=s.st||!s2.dur||s2.invis) +continue +not_alone=true +if(sy.voices[s2.v].rangeymin){ymin=s2.ymx +if(s2.dots) +dots=true}}else{if(s2.y>ymin) +ymin=s2.y}}} +if(!not_alone){s.y=12;s.ymx=24;s.ymn=0 +continue} +if(ymax==127&&s.y<12){shift=12-s.y +s.y+=shift;s.ymx+=shift;s.ymn+=shift} +if(ymin==-127&&s.y>12){shift=s.y-12 +s.y-=shift;s.ymx-=shift;s.ymn-=shift} +shift=ymax-s.ymx +if(shift<0){shift=Math.ceil(-shift/6)*6 +if(s.ymn-shift>=ymin){s.y-=shift;s.ymx-=shift;s.ymn-=shift +continue} +dx=dots?15:10;s.notes[0].shhd=dx;s.xmx=dx +continue} +shift=ymin-s.ymn +if(shift>0){shift=Math.ceil(shift/6)*6 +if(s.ymx+shift<=ymax){s.y+=shift;s.ymx+=shift;s.ymn+=shift +continue} +dx=dots?15:10;s.notes[0].shhd=dx;s.xmx=dx +continue}}} +function new_sym(s,p_v,last_s){s.p_v=p_v +s.v=p_v.v +s.st=p_v.st +s.time=last_s.time +if(p_v.last_sym){s.next=p_v.last_sym.next +if(s.next) +s.next.prev=s;p_v.last_sym.next=s;s.prev=p_v.last_sym} +p_v.last_sym=s;lktsym(s,last_s)} +function init_music_line(){var p_voice,s,s1,s2,s3,last_s,v,st,shr,shrmx,shl,shlp,p_st,top,nv=voice_tb.length,fmt=tsfirst.fmt +for(v=0;v=0&&s.a_meter.length +break} +if(s.part) +s.next.part=s.part +unlksym(s) +case C.TEMPO:case C.BLOCK:case C.REMARK:continue} +break}} +last_s=tsfirst +for(v=0;v0){s.nflags+=s.ntrem}else{if(s.nflags<=-2){s.stemless=true +s.prev.stemless=true} +s.nflags=s.ntrem} +s.prev.nflags=s.nflags} +for(s=p_voice.sym;s;s=s.next){if(s.type==C.NOTE){pitch=s.notes[0].pit +break}} +for(s=p_voice.sym;s;s=s.next){if(s.a_gch) +self.gch_build(s) +switch(s.type){case C.MREST:start_flag=true +break +case C.BAR:res=s.fmt.bardef[s.bar_type] +if(res) +s.bar_type=res +if(!s.beam_on) +start_flag=true +if(!s.next&&s.prev&&!s.invis&&s.prev.head==C.OVALBARS) +s.prev.head=C.SQUARE +break +case C.GRACE:for(s2=s.extra;s2;s2=s2.next){s2.notes.sort(abc2svg.pitcmp) +res=identify_note(s2,s2.dur_orig) +s2.head=res[0] +s2.dots=res[1] +s2.nflags=res[2] +if(s2.trem2&&(!s2.next||s2.next.trem2)) +trem_adj(s2)} +break +case C.NOTE:case C.REST:res=identify_note(s,s.dur_orig);s.head=res[0];s.dots=res[1];s.nflags=res[2] +if(s.nflags<=-2) +s.stemless=true +if(s.xstem) +s.nflags=0 +if(s.trem1){if(s.nflags>0) +s.nflags+=s.ntrem +else +s.nflags=s.ntrem} +if(s.next&&s.next.trem2) +break +if(s.trem2){trem_adj(s) +break} +nflags=s.nflags +if(s.ntrem) +nflags+=s.ntrem +if(s.type==C.REST&&s.beam_end&&!s.beam_on){start_flag=true} +if(start_flag||nflags<=0){if(lastnote){lastnote.beam_end=true;lastnote=null} +if(nflags<=0){s.beam_st=s.beam_end=true}else if(s.type==C.NOTE||s.beam_on){s.beam_st=true;start_flag=false}} +if(s.beam_end) +start_flag=true +if(s.type==C.NOTE||s.beam_on) +lastnote=s +break} +if(s.type==C.NOTE){if(s.nhd) +s.notes.sort(abc2svg.pitcmp) +pitch=s.notes[0].pit +for(s2=s.prev;s2;s2=s2.prev){if(s2.type!=C.REST) +break +s2.notes[0].pit=pitch}}else{if(!s.notes){s.notes=[] +s.notes[0]={} +s.nhd=0} +s.notes[0].pit=pitch}} +if(lastnote) +lastnote.beam_end=true} +function set_rb(p_voice){var s2,n,s=p_voice.sym +while(s){if(s.type!=C.BAR||!s.rbstart||s.norepbra){s=s.next +continue} +n=0;s2=null +for(s=s.next;s;s=s.next){if(s.type!=C.BAR) +continue +if(s.rbstop) +break +if(!s.next){s.rbstop=2 +break} +n++ +if(n==s.fmt.rbmin) +s2=s +if(n==s.fmt.rbmax){if(s2) +s=s2;s.rbstop=1 +break}}}} +var delpit=[0,-7,-14,0] +function set_global(){var p_voice,v,nv=voice_tb.length,sy=cur_sy,st=sy.nstaff +insert_meter=cfmt.writefields.indexOf('M')>=0 +while(1){sy=sy.next +if(!sy) +break +if(sy.nstaff>st) +st=sy.nstaff} +nstaff=st;check_end_bar() +for(v=0;v1){set_float() +if(glovar.mrest_p) +mrest_expand()} +if(glovar.ottava&&cfmt.sound!="play") +set_ottava();set_clefs();self.set_pitch(null)} +function get_lshift(){var st,v,p_v,p1,po,fnt,w,sy=cur_sy,lsh1=0,lsho=0,nv=voice_tb.length +function get_wx(p,wx){var w,j,i=0 +p+='\n' +while(1){j=p.indexOf("\n",i) +if(j<0) +break +w=strwh(p.slice(i,j))[0]+12 +if(w>wx) +wx=w +if(j<0) +break +i=j+1} +return wx} +for(v=0;vlsh1) +lsh1=w} +if(po){w=get_wx(po,lsho) +if(w>lsho) +lsho=w}} +w=0 +while(sy){for(st=0;st<=sy.nstaff;st++){if(sy.staves[st].flags&(OPEN_BRACE2|OPEN_BRACKET2)){w=12 +break} +if(sy.staves[st].flags&(OPEN_BRACE|OPEN_BRACKET)) +w=6} +if(w==12) +break +sy=sy.next} +lsh1+=w +lsho+=w +return[lsh1,lsho]} +function set_indent(lsh){var st,v,w,p_voice,p,i,j,font,vnt=0,fmt=tsnext?tsnext.fmt:cfmt +if(fmt.systnames){for(v=voice_tb.length;--v>=0;){p_voice=voice_tb[v] +if(!cur_sy.voices[v]||!gene.st_print[p_voice.st]) +continue +if(p_voice.nm&&(p_voice.new_name||fmt.systnames==2)){vnt=2 +break} +if(p_voice.snm) +vnt=1}} +gene.vnt=vnt +return vnt==2?lsh[0]:lsh[1]} +function set_beams(sym){var s,t,g,beam,s_opp,n,m,mid_p,pu,pd,laststem=-1 +for(s=sym;s;s=s.next){if(s.type!=C.NOTE){if(s.type!=C.GRACE) +continue +g=s.extra +if(g.stem==2){s_opp=s +continue} +if(!s.stem) +s.stem=s.multi||1 +for(;g;g=g.next){g.stem=s.stem;g.multi=s.multi} +continue} +if(!s.stem&&s.multi) +s.stem=s.multi +if(!s.stem){mid_p=s.mid/3+18 +if(beam){s.stem=laststem}else if(s.beam_st&&!s.beam_end){beam=true;pu=s.notes[s.nhd].pit;pd=s.notes[0].pit +for(g=s.next;g;g=g.next){if(g.type!=C.NOTE) +continue +if(g.stem||g.multi) +s.stem=g.stem||g.multi +if(g.notes[g.nhd].pit>pu) +pu=g.notes[g.nhd].pit +if(g.notes[0].pitmid_p*2){s.stem=-1}else{if(s.fmt.bstemdown) +s.stem=-1}} +if(!s.stem) +s.stem=laststem}else{n=(s.notes[s.nhd].pit+s.notes[0].pit)/2 +if(n==mid_p&&s.nhd>1){for(m=0;m=mid_p) +break} +n=m*2mid_p||s.fmt.bstemdown) +s.stem=-1 +else +s.stem=laststem}}else{if(s.beam_st&&!s.beam_end) +beam=true} +if(s.beam_end) +beam=false;laststem=s.stem;if(s_opp){for(g=s_opp.extra;g;g=g.next) +g.stem=-laststem;s_opp.stem=-laststem;s_opp=null}}} +function same_head(s1,s2){var i1,i2,l1,l2,head,i11,i12,i21,i22,sh1,sh2,shu=s1.fmt.shiftunison||0 +if(shu>=3) +return false +if((l1=s1.dur)>=C.BLEN) +return false +if((l2=s2.dur)>=C.BLEN) +return false +if(s1.stemless&&s2.stemless) +return false +if(s1.dots!=s2.dots){if(shu&1||s1.dots*s2.dots!=0) +return false} +if(s1.stem*s2.stem>0) +return false +i1=i2=0 +if(s1.notes[0].pit>s2.notes[0].pit){if(s1.stem<0) +return false +while(s2.notes[i2].pit!=s1.notes[0].pit){if(++i2>s2.nhd) +return false}}else if(s1.notes[0].pits1.nhd) +return false}} +if(s2.notes[i2].acc!=s1.notes[i1].acc) +return false;i11=i1;i21=i2;sh1=s1.notes[i1].shhd;sh2=s2.notes[i2].shhd +do{i1++;i2++ +if(i1>s1.nhd){break} +if(i2>s2.nhd){break} +if(s2.notes[i2].acc!=s1.notes[i1].acc) +return false +if(sh10) +return false}else if(i2<=s2.nhd){if(s1.stem>0) +return false} +i12=i1;i22=i2;head=0 +if(l1!=l2){if(l1=C.BLEN/2?2:1}else{return false}} +if(!head) +head=s1.p_v.scale-2){if(s.stem>0){w=-w;i=s.notes[0].pit*2;j=(Math.ceil((s.ymx-2)/3)+18)*2}else{i=(Math.ceil((s.ymn+2)/3)+18)*2;j=s.notes[s.nhd].pit*2} +if(i<0) +i=0 +if(j>=MAXPIT) +j=MAXPIT-1 +while(i<=j) +left[i++]=w} +shift=s.notes[s.stem>0?0:s.nhd].shhd;for(m=0;m<=s.nhd;m++){w=-s.notes[m].shhd+w_base+shift;i=s.notes[m].pit*2 +if(i<0) +i=0 +else if(i>=MAXPIT-1) +i=MAXPIT-2 +if(w>left[i]) +left[i]=w +if(s.head!=C.SQUARE) +w-=1 +if(w>left[i-1]) +left[i-1]=w +if(w>left[i+1]) +left[i+1]=w} +return left} +function set_right(s){var m,i,j,k,shift,w_base=w_note[s.head],w=w_base,flags=s.nflags>0&&s.beam_st&&s.beam_end,right=[] +for(i=0;i-2){if(s.stem<0){w=-w;i=(Math.ceil((s.ymn+2)/3)+18)*2;j=s.notes[s.nhd].pit*2;k=i+4}else{i=s.notes[0].pit*2;j=(Math.ceil((s.ymx-2)/3)+18)*2} +if(i<0) +i=0 +if(j>MAXPIT) +j=MAXPIT +while(i0){if(s.xmx==0) +i=s.notes[s.nhd].pit*2 +else +i=s.notes[0].pit*2;i+=4 +if(i<0) +i=0 +for(;i0?0:s.nhd].shhd +for(m=0;m<=s.nhd;m++){w=s.notes[m].shhd+w_base-shift;i=s.notes[m].pit*2 +if(i<0) +i=0 +else if(i>=MAXPIT-1) +i=MAXPIT-2 +if(w>right[i]) +right[i]=w +if(s.head!=C.SQUARE) +w-=1 +if(w>right[i-1]) +right[i-1]=w +if(w>right[i+1]) +right[i+1]=w} +return right} +function set_overlap(){var s,s1,s2,s3,i,i1,i2,m,sd,t,dp,d,d2,dr,dr2,dx,left1,right1,left2,right2,right3,pl,pr,sy=cur_sy +function v_invert(){s1=s2;s2=s;d=d2;pl=left1;pr=right1;dr2=dr} +for(s=tsfirst;s;s=s.ts_next){if(s.type!=C.NOTE||s.invis){if(s.type==C.STAVES) +sy=s.sy +continue} +if(s.xstem&&s.ts_prev.stem<0){for(m=0;m<=s.nhd;m++){s.notes[m].shhd-=7;s.notes[m].shac+=16}} +s2=s +while(1){s2=s2.ts_next +if(!s2) +break +if(s2.time!=s.time){s2=null +break} +if(s2.type==C.NOTE&&!s2.invis&&s2.st==s.st) +break} +if(!s2) +continue +s1=s +if(sy.voices[s1.v].ranges2.ymx||s1.ymx0&&s2.stem<0&&s1.notes[0].pit==s2.notes[s2.nhd].pit+1)||(s1.stem<0&&s2.stem>0&&s1.notes[s1.nhd].pit+1==s2.notes[0].pit)){if(s1.stem<0){s1=s2;s2=s} +d=s1.notes[0].shhd+7 +for(m=0;m<=s2.nhd;m++) +s2.notes[m].shhd+=d +s2.xmx+=d +s1.xmx=s2.xmx +continue} +right1=set_right(s1);left2=set_left(s2);s3=s1.ts_prev +if(s3&&s3.time==s1.time&&s3.st==s1.st&&s3.type==C.NOTE&&!s3.invis){right3=set_right(s3) +for(i=0;iright1[i]) +right1[i]=right3[i]}}else{s3=null} +d=-10 +for(i=0;id) +d=left2[i]+right1[i]} +if(d<-3&&((s2.notes[0].pit&1)||!(s1.dots||s2.dots)||(!(s1.notes[s1.nhd].pit==s2.notes[0].pit+2&&s1.dot_low)&&!(s1.notes[s1.nhd].pit+2==s2.notes[0].pit&&s2.dot_low)))) +continue +right2=set_right(s2);left1=set_left(s1) +if(s3){right3=set_left(s3) +for(i=0;ileft1[i]) +left1[i]=right3[i]}} +d2=dr=dr2=-100 +for(i=0;id2) +d2=left1[i]+right2[i] +if(right2[i]>dr2) +dr2=right2[i] +if(right1[i]>dr) +dr=right1[i]} +t=0;i1=s1.nhd;i2=s2.nhd +while(1){dp=s1.notes[i1].pit-s2.notes[i2].pit +switch(dp){case 2:if(!(s1.notes[i1].pit&1)) +s1.dot_low=false +break +case 1:if(s1.notes[i1].pit&1) +s2.dot_low=true +else +s1.dot_low=false +break +case 0:if(s1.notes[i1].acc!=s2.notes[i2].acc){t=-1 +break} +if(s2.notes[i2].acc){if(!s1.notes[i1].acc) +s1.notes[i1].acc=s2.notes[i2].acc +s2.notes[i2].acc=0} +if(s1.dots&&s2.dots&&(s1.notes[i1].pit&1)) +t=1 +break +case-1:if(s1.notes[i1].pit&1) +s2.dot_low=false +else +s1.dot_low=true +break +case-2:if(!(s1.notes[i1].pit&1)) +s2.dot_low=false +break} +if(t<0) +break +if(dp>=0){if(--i1<0) +break} +if(dp<=0){if(--i2<0) +break}} +if(t<0){unison_acc(s1,s2,i1,i2) +continue} +sd=0;if(s1.dots){if(s2.dots){if(!t) +sd=1}else{v_invert()}}else if(s2.dots){if(d2+dr=0?0:s1.nhd;d+=s1.notes[m].shhd;m=s2.stem>=0?0:s2.nhd;d-=s2.notes[m].shhd +if(s1.dots){dx=7.7+s1.xmx+ +3.5*s1.dots-3.5+ +3;if(!sd){d2=-100;for(i1=0;i1<=s1.nhd;i1++){i=s1.notes[i1].pit +if(!(i&1)){if(!s1.dot_low) +i++ +else +i--} +i*=2 +if(i<1) +i=1 +else if(i>=MAXPIT-1) +i=MAXPIT-2 +if(pl[i]>d2) +d2=pl[i] +if(pl[i-1]+1>d2) +d2=pl[i-1]+1 +if(pl[i+1]+1>d2) +d2=pl[i+1]+1} +if(dx+d2+2>d) +d=dx+d2+2}else{if(dx=MAXPIT-1) +i=MAXPIT-2 +if(pr[i]>d2) +d2=pr[i] +if(pr[i-1]+1>d2) +d2=pr[i-1]=1 +if(pr[i+1]+1>d2) +d2=pr[i+1]+1} +if(d2>4.5&&7.7+s1.xmx+2=0;m--){s2.notes[m].shhd+=d} +s2.xmx+=d +if(sd) +s1.xmx=s2.xmx}} +Abc.prototype.set_stems=function(){var s,s2,g,slen,scale,ymn,ymx,nflags,ymin,ymax +for(s=tsfirst;s;s=s.ts_next){if(s.type!=C.NOTE){if(s.type!=C.GRACE) +continue +ymin=ymax=s.mid +for(g=s.extra;g;g=g.next){slen=GSTEM +if(g.nflags>1) +slen+=1.2*(g.nflags-1);ymn=3*(g.notes[0].pit-18);ymx=3*(g.notes[g.nhd].pit-18) +if(s.stem>=0){g.y=ymn;g.ys=ymx+slen;ymx=Math.round(g.ys)}else{g.y=ymx;g.ys=ymn-slen;ymn=Math.round(g.ys)} +ymx+=4 +ymn-=4 +if(ymnymax) +ymax=ymx;g.ymx=ymx;g.ymn=ymn} +s.ymx=ymax;s.ymn=ymin +continue} +set_head_shift(s);nflags=s.nflags +if(s.beam_st&&!s.beam_end){if(s.feathered_beam) +nflags=++s.nflags +for(s2=s.next;;s2=s2.next){if(s2.type==C.NOTE){if(s.feathered_beam) +s2.nflags++ +if(s2.beam_end) +break}} +if(s2.nflags>nflags) +nflags=s2.nflags}else if(!s.beam_st&&s.beam_end){for(s2=s.prev;;s2=s2.prev){if(s2.beam_st) +break} +if(s2.nflags>nflags) +nflags=s2.nflags} +slen=s.fmt.stemheight +switch(nflags){case 2:slen+=0;break +case 3:slen+=4;break +case 4:slen+=8;break +case 5:slen+=12;break} +if((scale=s.p_v.scale)!=1) +slen*=(scale+1)*.5;ymn=3*(s.notes[0].pit-18) +if(s.nhd>0){slen-=2;ymx=3*(s.notes[s.nhd].pit-18)}else{ymx=ymn} +if(s.ntrem) +slen+=2*s.ntrem +if(s.stemless){if(s.stem>=0){s.y=ymn;s.ys=ymx}else{s.ys=ymn;s.y=ymx} +s.ymx=ymx+4;s.ymn=ymn-4}else if(s.stem>=0){if(s.notes[s.nhd].pit>26&&(nflags<=0||!s.beam_st||!s.beam_end)){slen-=2 +if(s.notes[s.nhd].pit>28) +slen-=2} +s.y=ymn +if(s.notes[0].tie) +ymn-=3;s.ymn=ymn-4;s.ys=ymx+slen +if(s.yss.mid) +s.ys=s.mid;s.ymn=(s.ys-2.5)|0;s.y=ymx +if(s.notes[s.nhd].tie) +ymx+=3;s.ymx=ymx+4}}} +var blocks=[] +Abc.prototype.block_gen=function(s){switch(s.subtype){case"leftmargin":case"rightmargin":case"pagescale":case"pagewidth":case"scale":case"staffwidth":self.set_format(s.subtype,s.param) +break +case"mc_start":if(multicol){error(1,s,"No end of the previous %%multicol") +break} +multicol={posy:posy,maxy:posy,lm:cfmt.leftmargin,rm:cfmt.rightmargin,w:cfmt.pagewidth,sc:cfmt.scale} +break +case"mc_new":if(!multicol){error(1,s,"%%multicol new without start") +break} +if(posy>multicol.maxy) +multicol.maxy=posy +cfmt.leftmargin=multicol.lm +cfmt.rightmargin=multicol.rm +cfmt.pagewidth=multicol.w +cfmt.scale=multicol.sc +posy=multicol.posy +img.chg=1 +break +case"mc_end":if(!multicol){error(1,s,"%%multicol end without start") +break} +if(posy') +blkdiv=2 +break +case"sep":set_page();vskip(s.sk1);output+='\n';vskip(s.sk2);break +case"text":set_font(s.font) +use_font(s.font) +write_text(s.text,s.opt) +break +case"title":write_title(s.text,true) +break +case"vskip":vskip(s.sk);break}} +function sym_staff_move(st){for(var s=tsfirst;s;s=s.ts_next){if(s.nl) +break +if(s.st==st&&s.type!=C.CLEF){s.st++ +if(s.type!=C.TEMPO) +s.invis=true}}} +function set_piece(){var s,last,p_voice,st,v,nv,tmp,non_empty,non_empty_gl=[],sy=cur_sy +function reset_staff(st){var p_staff=staff_tb[st],sy_staff=sy.staves[st] +if(!p_staff) +p_staff=staff_tb[st]={} +p_staff.y=0;p_staff.stafflines=sy_staff.stafflines;p_staff.staffscale=sy_staff.staffscale;p_staff.ann_top=p_staff.ann_bot=0} +function set_brace(){var st,i,empty_fl,n=sy.staves.length +for(st=0;st=l-2){if(p_staff.stafflines[i]!='.'){p_staff.botbar-=6;p_staff.topbar+=6}else{p_staff.botbar-=12;p_staff.topbar+=12 +continue}} +if(!non_empty_gl[st]) +continue +p_staff.hll=17+i*2 +p_staff.hlmap=new Int8Array((l-i+1)*2 ++2) +for(j=1;instaff){switch(s.type){case C.CLEF:staff_tb[st].clef=s +break +case C.KEY:s.p_v.ckey=s +break +case C.METER:s.p_v.meter=s +break} +unlksym(s) +continue} +if(non_empty[st]) +continue +switch(s.type){default:continue +case C.BAR:if(s.bar_mrep||sy.staves[st].staffnonote>1) +break +continue +case C.GRACE:break +case C.NOTE:case C.REST:case C.SPACE:case C.MREST:if(sy.staves[st].staffnonote>1) +break +if(s.invis) +continue +if(sy.staves[st].staffnonote||s.type==C.NOTE) +break +continue} +non_empty_gl[st]=non_empty[st]=true} +tsnext=s;set_brace() +sy.st_print=non_empty +set_top_bot() +for(st=0;stwidth){error(1,s,"Line too much shrunk $1 $2 $3",xmin.toFixed(1),xx.toFixed(1),width.toFixed(1)) +break} +if(s.space){if(s.space=width){x=0 +for(;s;s=s.ts_next){if(s.seqst) +x+=s.shrink;s.x=x} +spf_last=0}else if((xx0+xx)/2+xs>width||stretch){while(--cnt>=0){if(xx==xse) +xx+=5 +spf=(width-xs-xse)/(xx-xse) +xx=0;xse=0;x=0 +for(s=tsfirst;s;s=s.ts_next){if(s.seqst){if(s.space){if(s.space*spf<=s.shrink){xse+=s.shrink;xx+=s.shrink;x+=s.shrink}else{xx+=s.space;x+=s.space*spf}}else{x+=s.shrink}} +s.x=x} +if(Math.abs(x-width)<0.1) +break} +spf_last=width/(xx+xs)}else{spf=spf_last +if(spf<1-s.fmt.maxshrink) +spf=1-s.fmt.maxshrink +if(ll&&spf(width-xs)/xx) +spf=(width-xs)/xx +x=0 +for(;s;s=s.ts_next){if(s.seqst) +x+=s.space?(s.space*spf<=s.shrink?s.shrink:s.space*spf):s.shrink +s.x=x}} +realwidth=x +for(s=some_grace;s;s=s.ts_next){if(s.type!=C.GRACE) +continue +if(s.gr_shift) +x=s.prev.x+s.prev.wr +else +x=s.x-s.wl +for(g=s.extra;g;g=g.next) +g.x+=x}} +function set_sym_line(){var p_v,s,v=voice_tb.length +while(--v>=0){p_v=voice_tb[v] +if(p_v.sym&&p_v.s_prev){p_v.sym.prev=p_v.s_prev +p_v.s_prev.next=p_v.sym} +s=p_v.s_next +p_v.s_next=null +p_v.sym=s +if(s){if(s.prev) +s.prev.next=s +p_v.s_prev=s.prev +s.prev=null}else{p_v.s_prev=null}}} +function set_posx(){posx=img.lm/cfmt.scale} +function gen_init(){var s=tsfirst,tim=s.time +for(;s;s=s.ts_next){if(s.time!=tim){set_page() +return} +switch(s.type){case C.NOTE:case C.REST:case C.MREST:case C.SPACE:set_page() +return +default:continue +case C.STAVES:cur_sy=s.sy +continue +case C.BLOCK:if(s.play) +continue +self.block_gen(s) +break} +unlksym(s) +if(s.p_v.s_next==s) +s.p_v.s_next=s.next} +tsfirst=null} +Abc.prototype.output_music=function(){var v,lwidth,indent,lsh,line_height,ts1st,tslast,p_v,nv=voice_tb.length +set_global() +if(nv>1) +self.set_stem_dir() +for(v=0;v1){set_rest_offset();set_overlap()} +set_allsymwidth(1) +gen_init() +if(!tsfirst) +return +lsh=get_lshift() +if(cfmt.singleline){v=get_ck_width();lwidth=lsh[0]+v[0]+v[1]+get_width(tsfirst,null)[0] +v=cfmt.singleline==2?get_lwidth():lwidth +if(v>lwidth) +lwidth=v +else +img.width=lwidth*cfmt.scale+img.lm+img.rm+2}else{lwidth=get_lwidth();cut_tune(lwidth,lsh)} +ts1st=tsfirst +v=nv +while(--v>=0) +voice_tb[v].osym=voice_tb[v].sym +spf_last=0 +while(1){set_piece();indent=set_indent(lsh) +if(!line_height&&cfmt.indent&&indent=0){p_v=voice_tb[v] +if(p_v.sym&&p_v.s_prev) +p_v.sym.prev=p_v.s_prev +p_v.sym=p_v.osym}} +var a_gch,a_dcn=[],multicol,maps={} +var qplet_tb=new Int8Array([0,1,3,2,3,0,2,0,3,0]),ntb="CDEFGABcdefgab" +function set_ref(s){s.fname=parse.fname;s.istart=parse.istart;s.iend=parse.iend} +function new_clef(clef_def){var s={type:C.CLEF,clef_line:2,clef_type:"t",v:curvoice.v,p_v:curvoice,time:curvoice.time,dur:0},i=1 +set_ref(s) +switch(clef_def[0]){case'"':i=clef_def.indexOf('"',1);s.clef_name=clef_def.slice(1,i);i++ +break +case'a':if(clef_def[1]=='u'){s.clef_type="a";s.clef_auto=true;i=4 +break} +i=4 +case'C':s.clef_type="c";s.clef_line=3 +break +case'b':i=4 +case'F':s.clef_type="b";s.clef_line=4 +break +case'n':i=4 +s.invis=true +s.clef_none=1 +break +case't':if(clef_def[1]=='e'){s.clef_type="c";s.clef_line=4 +break} +i=6 +case'G':break +case'p':i=4 +case'P':s.clef_type="p";s.clef_line=3;break +default:syntax(1,"Unknown clef '$1'",clef_def) +return} +if(clef_def[i]>='1'&&clef_def[i]<='9'){s.clef_line=+clef_def[i] +i++} +delete curvoice.snd_oct +if(clef_def[i+1]!='8'&&clef_def[i+1]!='1') +return s +switch(clef_def[i]){case'^':s.clef_oct_transp=true +case'+':s.clef_octave=clef_def[i+1]=='8'?7:14 +if(!s.clef_oct_transp) +curvoice.snd_oct=clef_def[i+1]==8?12:24 +break +case'_':s.clef_oct_transp=true +case'-':s.clef_octave=clef_def[i+1]=='8'?-7:-14 +if(!s.clef_oct_transp) +curvoice.snd_oct=clef_def[i+1]==8?-12:-24 +break} +return s} +function get_interval(param,score){var i,val,tmp,note,pit +tmp=new scanBuf;tmp.buffer=param +pit=[] +for(i=0;i<2;i++){note=tmp.buffer[tmp.index]?parse_acc_pit(tmp):null +if(!note){if(i!=1||!score){syntax(1,errs.bad_transp) +return} +pit[i]=242}else{if(typeof note.acc=="object"){syntax(1,errs.bad_transp) +return} +pit[i]=abc2svg.pab40(note.pit,note.acc)}} +return pit[1]-pit[0]} +function nt_trans(nt,a){var ak,an,d,b40,n +if(typeof a=="object"){n=a[0] +d=a[1] +a=n>0?1:-1} +b40=abc2svg.pab40(nt.pit,a) ++curvoice.tr_sco +nt.pit=abc2svg.b40p(b40) +an=abc2svg.b40a(b40) +if(!d){if(an==-3) +return an +a=an +if(nt.acc){if(!a) +a=3}else{if(!curvoice.ckey.k_none) +a=0} +nt.acc=a +return an} +switch(an){case-2:if(n>0) +n-=d*2 +else +n-=d +break +case-1:if(n>0) +n-=d +break +case 0:case 3:if(n>0) +n-=d +else +n+=d +break +case 1:if(n<0) +n+=d +break +case 2:if(n<0) +n+=d*2 +else +n+=d +break} +nt.acc=[n,d] +return an} +function set_linebreak(param){var i,item +for(i=0;i<128;i++){if(char_tb[i]=="\n") +char_tb[i]=nil} +param=param.split(/\s+/) +for(i=0;i":continue +case"":item='\n' +break +default:syntax(1,"Bad value '$1' in %%linebreak - ignored",item) +continue} +char_tb[item.charCodeAt(0)]='\n'}} +function set_user(parm){var k,c,v,a=parm.match(/(.)[=\s]*(\[I:.+\]|".+"|!.+!)$/) +if(!a){syntax(1,'Lack of starting [, ! or " in U: / %%user') +return} +c=a[1];v=a[2] +if(c[0]=='\\'){if(c[1]=='t') +c='\t' +else if(!c[1]) +c=' '} +k=c.charCodeAt(0) +if(k>=128){syntax(1,errs.not_ascii) +return} +switch(char_tb[k][0]){case'0':case'd':case'i':case' ':break +case'"':case'!':case'[':if(char_tb[k].length>1) +break +default:syntax(1,"Bad user character '$1'",c) +return} +switch(v){case"!beambreak!":v=" " +break +case"!ignore!":v="i" +break +case"!nil!":case"!none!":v="d" +break} +char_tb[k]=v} +function get_st_lines(param){if(!param) +return +if(/^[\]\[|.-]+$/.test(param)) +return param.replace(/\]/g,'[') +var n=+param +switch(n){case 0:return"..." +case 1:return"..|" +case 2:return".||" +case 3:return".|||"} +if(isNaN(n)||n<0||n>16) +return +return"||||||||||||||||".slice(0,n)} +function new_block(subtype){var c_v,s={type:C.BLOCK,subtype:subtype,dur:0} +c_v=curvoice +if(subtype.slice(0,4)!="midi") +curvoice=voice_tb[0] +sym_link(s) +if(c_v) +curvoice=c_v +return s} +Abc.prototype.set_vp=function(a){var s,item,pos,val,clefpit,tr_p=0 +while(1){item=a.shift() +if(!item) +break +if(item.slice(-1)=='='&&!a.length){syntax(1,errs.bad_val,item) +break} +switch(item){case"clef=":s=a.shift() +break +case"clefpitch=":item=a.shift() +if(item){val=ntb.indexOf(item[0]) +if(val>=0){switch(item[1]){case"'":val+=7 +break +case',':val-=7 +if(item[2]==',') +val-=7 +break} +clefpit=4-val +break}} +syntax(1,errs.bad_val,item) +break +case"octave=":val=+a.shift() +if(isNaN(val)) +syntax(1,errs.bad_val,item) +else +curvoice.octave=val +break +case"cue=":curvoice.scale=a.shift()=='on'?.7:1 +break +case"instrument=":item=a.shift() +val=item.indexOf('/') +if(val<0){val=get_interval('c'+item) +if(val==undefined) +break +curvoice.sound=val +tr_p|=2 +val=0}else{val=get_interval('c'+item.slice(val+1)) +if(val==undefined) +break +curvoice.sound=val +tr_p|=2 +val=get_interval(item.replace('/','')) +if(val==undefined) +break} +curvoice.score=cfmt.sound?curvoice.sound:val +tr_p|=1 +break +case"map=":curvoice.map=a.shift() +break +case"name=":case"nm=":curvoice.nm=a.shift() +if(curvoice.nm[0]=='"') +curvoice.nm=cnv_escape(curvoice.nm.slice(1,-1)) +curvoice.new_name=true +break +case"stem=":case"pos=":if(item=="pos=") +item=a.shift().slice(1,-1).split(' ') +else +item=["stm",a.shift()];val=posval[item[1]] +if(val==undefined){syntax(1,errs.bad_val,"%%pos") +break} +switch(item[2]){case"align":val|=C.SL_ALIGN;break +case"center":val|=C.SL_CENTER;break +case"close":val|=C.SL_CLOSE;break} +if(!pos) +pos={} +pos[item[0]]=val +break +case"scale=":val=+a.shift() +if(isNaN(val)||val<.5||val>2) +syntax(1,errs.bad_val,"%%voicescale") +else +curvoice.scale=val +break +case"score=":if(cfmt.nedo){syntax(1,errs.notransp) +break} +item=a.shift() +if(cfmt.sound) +break +val=get_interval(item,true) +if(val!=undefined){curvoice.score=val +tr_p|=1} +break +case"shift=":if(cfmt.nedo){syntax(1,errs.notransp) +break} +val=get_interval(a.shift()) +if(val!=undefined){curvoice.shift=val +tr_p=3} +break +case"sound=":if(cfmt.nedo){syntax(1,errs.notransp) +break} +val=get_interval(a.shift()) +if(val==undefined) +break +curvoice.sound=val +if(cfmt.sound) +curvoice.score=val +tr_p|=2 +break +case"subname=":case"sname=":case"snm=":curvoice.snm=a.shift() +if(curvoice.snm[0]=='"') +curvoice.snm=curvoice.snm.slice(1,-1);break +case"stafflines=":val=get_st_lines(a.shift()) +if(val==undefined) +syntax(1,"Bad %%stafflines value") +else if(curvoice.st!=undefined) +par_sy.staves[curvoice.st].stafflines=val +else +curvoice.stafflines=val +break +case"staffnonote=":val=+a.shift() +if(isNaN(val)) +syntax(1,"Bad %%staffnonote value") +else +curvoice.staffnonote=val +break +case"staffscale=":val=+a.shift() +if(isNaN(val)||val<.3||val>2) +syntax(1,"Bad %%staffscale value") +else +curvoice.staffscale=val +break +case"tacet=":val=a.shift() +curvoice.tacet=val||undefined +break +case"transpose=":if(cfmt.nedo){syntax(1,errs.notransp) +break} +val=get_transp(a.shift()) +if(val==undefined){syntax(1,errs.bad_transp)}else{curvoice.sound=val +if(cfmt.sound) +curvoice.score=val +tr_p=2} +break +default:switch(item.slice(0,4)){case"treb":case"bass":case"alto":case"teno":case"perc":s=item +break +default:if("GFC".indexOf(item[0])>=0) +s=item +else if(item.slice(-1)=='=') +a.shift() +break} +break}} +if(pos){curvoice.pos=clone(curvoice.pos) +for(item in pos) +if(pos.hasOwnProperty(item)) +curvoice.pos[item]=pos[item]} +if(s){s=new_clef(s) +if(s){if(clefpit) +s.clefpit=clefpit +get_clef(s)}} +if(tr_p&2){tr_p=(curvoice.sound|0)+(curvoice.shift|0) +if(tr_p) +curvoice.tr_snd=abc2svg.b40m(tr_p+122)-36 +else if(curvoice.tr_snd) +curvoice.tr_snd=0}} +function set_kv_parm(a){if(!curvoice.init){curvoice.init=true +if(info.V){if(info.V[curvoice.id]) +a=info.V[curvoice.id].concat(a) +if(info.V['*']) +a=info.V['*'].concat(a)}} +if(a.length) +self.set_vp(a)} +function memo_kv_parm(vid,a){if(!a.length) +return +if(!info.V) +info.V={} +if(info.V[vid]) +Array.prototype.push.apply(info.V[vid],a) +else +info.V[vid]=a} +function new_key(param){var i,key_end,c,tmp,note,sf="FCGDAEB".indexOf(param[0])-1,mode=0,s={type:C.KEY,dur:0} +set_ref(s);i=1 +if(sf<-1){switch(param[0]){case'H':key_end=true +if(param[1].toLowerCase()!='p'){syntax(1,"Unknown bagpipe-like key") +break} +s.k_bagpipe=param[1];sf=param[1]=='P'?0:2;i++ +if(!cfmt.temper) +cfmt.temper=new Float32Array([11.62,12.55,1.66,2.37,3.49,0,1.66,2.37,3.49,4.41,5.53,0,3.49,4.41,5.53,6.63,7.35,4.41,5.53,6.63,7.35,8.19,0,6.63,7.35,8.19,9.39,10.51,0,8.19,9.39,10.51,11.62,12.55,0,10.51,11.62,12.55,1.66,1.66]) +break +case'P':syntax(1,"K:P is deprecated");sf=0;s.k_drum=true;key_end=true +break +case'n':if(param.indexOf("none")==0){sf=0;s.k_none=true;i=4 +break} +default:s.k_map=[] +s.k_mode=0 +return[s,info_split(param)]}} +if(!key_end){switch(param[i]){case'#':sf+=7;i++;break +case'b':sf-=7;i++;break} +param=param.slice(i).trim() +switch(param.slice(0,3).toLowerCase()){default:if(param[0]!='m'||(param[1]!=' '&¶m[1]!='\t'&¶m[1]!='\n')){key_end=true +break} +case"aeo":case"m":case"min":sf-=3;mode=5 +break +case"dor":sf-=2;mode=1 +break +case"ion":case"maj":break +case"loc":sf-=5;mode=6 +break +case"lyd":sf+=1;mode=3 +break +case"mix":sf-=1;mode=4 +break +case"phr":sf-=4;mode=2 +break} +if(!key_end) +param=param.replace(/\w+\s*/,'') +if(param.indexOf("exp ")==0){param=param.replace(/\w+\s*/,'') +if(!param) +syntax(1,"No accidental after 'exp'");s.exp=1} +c=param[0] +if(c=='^'||c=='_'||c=='='){s.k_a_acc=[];tmp=new scanBuf;tmp.buffer=param +do{note=parse_acc_pit(tmp) +if(!note) +break +s.k_a_acc.push(note);c=param[tmp.index] +while(c==' ') +c=param[++tmp.index]}while(c=='^'||c=='_'||c=='=');param=param.slice(tmp.index)}else if(s.exp&¶m.indexOf("none")==0){sf=0 +param=param.replace(/\w+\s*/,'')}} +if(sf<-7||sf>7){syntax(1,"Key with double sharps/flats") +if(sf>7) +sf-=12 +else +sf+=12} +s.k_sf=sf;s.k_map=s.k_bagpipe&&!sf?abc2svg.keys[9]:abc2svg.keys[sf+7] +if(s.k_a_acc){s.k_map=new Int8Array(s.k_map) +i=s.k_a_acc.length +while(--i>=0){note=s.k_a_acc[i] +s.k_map[(note.pit+19)%7]=note.acc}} +s.k_mode=mode +s.k_b40=[1,24,7,30,13,36,19,2,25,8,31,14,37,20,3][sf+7] +return[s,info_split(param)]} +function new_meter(p){var p_v,s={type:C.METER,dur:0,a_meter:[]},meter={},val,v,m1=0,m2,i=0,j,wmeasure,in_parenth;set_ref(s) +if(p.indexOf("none")==0){i=4;wmeasure=1}else{wmeasure=0 +while(i'9'){syntax(1,"Bad char '$1' in M:",p[i]) +return} +m2=2;meter.top=p[i++] +for(;;){while(p[i]>='0'&&p[i]<='9') +meter.top+=p[i++] +if(p[i]==')'){if(p[i+1]!='/') +break +i++} +if(p[i]=='/'){i++;if(p[i]<='0'||p[i]>'9'){syntax(1,"Bad char '$1' in M:",p[i]) +return} +meter.bot=p[i++] +while(p[i]>='0'&&p[i]<='9') +meter.bot+=p[i++] +break} +if(p[i]!=' '&&p[i]!='+') +break +if(i>=p.length||p[i+1]=='(') +break +meter.top+=p[i++]} +m1=+meter.top +break} +if(!in_parenth){if(meter.bot) +m2=+meter.bot +wmeasure+=m1*C.BLEN/m2} +s.a_meter.push(meter);meter={} +while(p[i]==' ') +i++ +if(p[i]=='+'){meter.top=p[i++];s.a_meter.push(meter);meter={}}}} +if(p[i]=='='){val=p.substring(++i).match(/^(\d+)\/(\d+)$/) +if(!val){syntax(1,"Bad duration '$1' in M:",p.substring(i)) +return} +wmeasure=C.BLEN*val[1]/val[2]} +if(!wmeasure){syntax(1,errs.bad_val,'M:') +return} +s.wmeasure=wmeasure +if(cfmt.writefields.indexOf('M')<0) +s.a_meter=[] +if(parse.state!=3){info.M=p;glovar.meter=s +if(parse.state){if(!glovar.ulen){if(wmeasure<=1||wmeasure>=C.BLEN*3/4) +glovar.ulen=C.BLEN/8 +else +glovar.ulen=C.BLEN/16} +for(v=0;v0){d=text.slice(0,i).split(/\s+/) +text=text.slice(i+1).replace(/^\s+/,'') +while(1){c=d.shift() +if(!c) +break +nd=get_nd(c) +if(!nd) +return +if(!s.tempo_notes) +s.tempo_notes=[] +s.tempo_notes.push(nd)} +if(text.slice(0,4)=="ca. "){s.tempo_ca='ca. ' +text=text.slice(4)} +i=text.indexOf('/') +if(i>0){nd=get_nd(text) +if(!nd) +return +s.new_beat=nd}else{s.tempo=+text +if(!s.tempo||isNaN(s.tempo)){syntax(1,"Bad tempo value") +return}}} +if(parse.state<2||(!curvoice.time&&!glovar.tempo)){info.Q=txt +glovar.tempo=s +return} +if(!glovar.tempo) +syntax(0,"No previous tempo") +if(new_ctrl(s)) +sym_link(s)} +function do_info(info_type,text){var s,d1,d2,a,vid,tim,v,p_v +if(curvoice&&curvoice.ignore){switch(info_type){default:return +case'P':case'Q':case'V':break}} +switch(info_type){case'I':self.do_pscom(text) +break +case'L':a=text.match(/^1\/(\d+)(=(\d+)\/(\d+))?$/) +if(a){d1=+a[1] +if(!d1||(d1&(d1-1))!=0) +break +d1=C.BLEN/d1 +if(a[2]){d2=+a[4] +d2=d2?+a[3]/d2*C.BLEN:0}else{d2=d1}}else if(text=="auto"){d1=d2=-1} +if(!d2){syntax(1,"Bad L: value") +break} +if(parse.state<=1){glovar.ulen=d1}else{curvoice.ulen=d1;curvoice.dur_fact=d2/d1} +break +case'M':new_meter(text) +break +case'U':set_user(text) +break +case'P':if(!parse.state) +break +if(parse.state==1){info.P=text +break} +s={type:C.PART,text:text,time:tim} +if(!new_ctrl(s)) +break +sym_link(s) +if(cfmt.writefields.indexOf(info_type)<0) +s.invis=true +break +case'Q':if(!parse.state) +break +new_tempo(text) +break +case'V':get_voice(text) +if(parse.state==3) +curvoice.ignore=!par_sy.voices[curvoice.v] +break +case'K':if(!parse.state) +break +get_key(text) +break +case'N':case'R':if(!info[info_type]) +info[info_type]=text +else +info[info_type]+='\n'+text +break +case'r':if(!user.keep_remark||parse.state!=3) +break +s={type:C.REMARK,text:text,dur:0} +sym_link(s) +break +default:syntax(0,"'$1:' line ignored",info_type) +break}} +function adjust_dur(s){var s2,time,auto_time,i,fac;s2=curvoice.last_sym +if(!s2) +return;if(s2.type==C.MREST||s2.type==C.BAR) +return +while(s2.type!=C.BAR&&s2.prev) +s2=s2.prev;time=s2.time;auto_time=curvoice.time-time +fac=curvoice.wmeasure/auto_time +if(fac==1) +return +for(;s2;s2=s2.next){s2.time=time +if(!s2.dur||s2.grace) +continue +s2.dur*=fac;s2.dur_orig*=fac;time+=s2.dur +if(s2.type!=C.NOTE&&s2.type!=C.REST) +continue +for(i=0;i<=s2.nhd;i++) +s2.notes[i].dur*=fac} +curvoice.time=s.time=time} +function new_bar(){var s2,c,bar_type,line=parse.line,s={type:C.BAR,fname:parse.fname,istart:parse.bol+line.index,dur:0,multi:0} +if(vover&&vover.bar) +get_vover('|') +if(glovar.new_nbar){s.bar_num=glovar.new_nbar;glovar.new_nbar=0} +bar_type=line.char() +while(1){c=line.next_char() +switch(c){case'|':case'[':case']':case':':bar_type+=c +continue} +break} +if(bar_type[0]==':'){if(bar_type==':'){bar_type='|';s.bar_dotted=true}else{s.rbstop=2}} +if(a_gch) +csan_add(s) +if(a_dcn.length) +deco_cnv(s) +if(bar_type.slice(-1)=='['&&!(/[0-9" ]/.test(c))){bar_type=bar_type.slice(0,-1);line.index--;c='['} +if(c>'0'&&c<='9'){s.text=c +while(1){c=line.next_char() +if("0123456789,.-".indexOf(c)<0) +break +s.text+=c}}else if(c=='"'&&bar_type.slice(-1)=='['){s.text="" +while(1){c=line.next_char() +if(!c){syntax(1,"No end of repeat string") +return} +if(c=='"'){line.index++ +break} +s.text+=c}} +if(bar_type[0]==']'){s.rbstop=2 +if(bar_type.length!=1) +bar_type=bar_type.slice(1) +else +s.invis=true} +s.iend=parse.bol+line.index +if(s.text&&bar_type.slice(-1)=='['&&bar_type!='[') +bar_type=bar_type.slice(0,-1) +if(bar_type.slice(-1)==':'){s.rbstop=1 +if(s.text){syntax(1,"Variant ending on a left repeat bar") +delete s.text} +curvoice.tie_s_rep=null} +if(s.text){s.rbstart=s.rbstop=2 +if(s.text[0]=='1'){curvoice.tie_s_rep=curvoice.tie_s +if(curvoice.acc_tie) +curvoice.acc_tie_rep=curvoice.acc_tie.slice() +else if(curvoice.acc_tie_rep) +curvoice.acc_tie_rep=null}else{curvoice.tie_s=curvoice.tie_s_rep +if(curvoice.acc_tie_rep) +curvoice.acc_tie=curvoice.acc_tie_rep.slice()} +if(curvoice.norepbra&&!curvoice.second) +s.norepbra=1} +if(curvoice.ulen<0) +adjust_dur(s);if((bar_type=="["||bar_type=="|:")&&!curvoice.eoln&&!s.a_gch&&!s.invis){s2=curvoice.last_sym +if(s2&&s2.type==C.BAR){if((bar_type=="["&&!s2.text)||s.norepbra){if(s.text){s2.text=s.text +if(curvoice.st&&!s.norepbra&&!(par_sy.staves[curvoice.st-1].flags&STOP_BAR)) +s2.xsh=4} +if(s.norepbra) +s2.norepbra=1 +if(s.rbstart) +s2.rbstart=s.rbstart +if(s.rbstop) +s2.rbstop=s.rbstop +return} +if(bar_type=="|:"){switch(s2.bar_type){case":|":s2.bar_type="::";s2.rbstop=2 +return}}}} +switch(bar_type){case"[":case"[]":case"[|]":s.invis=true;bar_type=s.rbstart?"[":"[]" +break +case":|:":case":||:":bar_type="::" +break +case"||":if(cfmt["abc-version"]>="2.2") +break +case"[|":case"|]":s.rbstop=2 +break} +s.bar_type=bar_type +if(!curvoice.lyric_restart) +curvoice.lyric_restart=s +if(!curvoice.sym_restart) +curvoice.sym_restart=s +sym_link(s);s.st=curvoice.st +if(s.text&&s.st>0&&!s.norepbra&&!(par_sy.staves[s.st-1].flags&STOP_BAR)&&bar_type!='[') +s.xsh=4 +if(!s.bar_dotted&&!s.invis) +curvoice.acc=[]} +function parse_staves(p){var v,vid,vids={},a_vf=[],err=false,flags=0,brace=0,bracket=0,parenth=0,flags_st=0,e,a=p.match(/[^[\]|{}()*+\s]+|[^\s]/g) +if(!a){syntax(1,errs.bad_val,"%%score") +return} +while(1){e=a.shift() +if(!e) +break +switch(e){case'[':if(parenth||brace+bracket>=2){syntax(1,errs.misplaced,'[');err=true +break} +flags|=brace+bracket==0?OPEN_BRACKET:OPEN_BRACKET2;bracket++;flags_st<<=8;flags_st|=OPEN_BRACKET +break +case'{':if(parenth||brace||bracket>=2){syntax(1,errs.misplaced,'{');err=true +break} +flags|=!bracket?OPEN_BRACE:OPEN_BRACE2;brace++;flags_st<<=8;flags_st|=OPEN_BRACE +break +case'(':if(parenth){syntax(1,errs.misplaced,'(');err=true +break} +flags|=OPEN_PARENTH;parenth++;flags_st<<=8;flags_st|=OPEN_PARENTH +break +case'*':if(brace&&!parenth&&!(flags&(OPEN_BRACE|OPEN_BRACE2))) +flags|=FL_VOICE +break +case'+':flags|=MASTER_VOICE +break +case']':case'}':case')':syntax(1,"Bad voice ID in %%score");err=true +break +default:vid=e +while(1){e=a.shift() +if(!e) +break +switch(e){case']':if(!(flags_st&OPEN_BRACKET)){syntax(1,errs.misplaced,']');err=true +break} +bracket--;flags|=brace+bracket==0?CLOSE_BRACKET:CLOSE_BRACKET2;flags_st>>=8 +continue +case'}':if(!(flags_st&OPEN_BRACE)){syntax(1,errs.misplaced,'}');err=true +break} +brace--;flags|=!bracket?CLOSE_BRACE:CLOSE_BRACE2;flags&=~FL_VOICE;flags_st>>=8 +continue +case')':if(!(flags_st&OPEN_PARENTH)){syntax(1,errs.misplaced,')');err=true +break} +parenth--;flags|=CLOSE_PARENTH;flags_st>>=8 +continue +case'|':flags|=STOP_BAR +continue} +break} +if(vids[vid]){syntax(1,"Double voice in %%score") +err=true}else{vids[vid]=true +a_vf.push([vid,flags])} +flags=0 +if(!e) +break +a.unshift(e) +break}} +if(flags_st!=0){syntax(1,"'}', ')' or ']' missing in %%score");err=true} +if(err||!a_vf.length) +return +return a_vf} +function info_split(text){if(!text) +return[] +var a=text.match(/[^\s"=]+=?|"[^"]*"/g) +if(!a){syntax(1,"Unterminated string") +return[]} +return a} +var reg_dur=/(\d*)(\/*)(\d*)/g +function parse_dur(line){var res,num,den;reg_dur.lastIndex=line.index;res=reg_dur.exec(line.buffer) +if(!res[0]) +return[1,1];num=res[1]||1;den=res[3]||1 +if(!res[3]) +den*=1<='1'&&c<='9')||c=='/'){nd=parse_dur(line);if(acc<0) +nd[0]=-nd[0] +if(cfmt.nedo&&nd[1]==1){nd[0]*=12 +nd[1]*=cfmt.nedo} +acc=nd +c=line.char()}} +pit=ntb.indexOf(c)+16;c=line.next_char() +if(pit<16){syntax(1,"'$1' is not a note",line.buffer[line.index-1]) +return} +while(c=="'"){pit+=7;c=line.next_char()} +while(c==','){pit-=7;c=line.next_char()} +note={pit:pit,shhd:0,shac:0} +if(acc) +note.acc=acc +return note} +function set_map(note,acc){var pit=note.pit,nn=not2abc(pit,acc),map=maps[curvoice.map] +if(!map[nn]){nn='o'+nn.replace(/[',]+/,'') +if(!map[nn]){nn='k'+ntb[(pit+75- +curvoice.ckey.k_sf*11)%7] +if(!map[nn]){nn='all' +if(!map[nn]) +return}}} +note.map=map=map[nn] +if(map[1]){note.pit=pit=map[1].pit +note.acc=map[1].acc +if(map[1].notrp){note.notrp=1 +note.noplay=1}} +if(map[2]) +note.color=map[2] +nn=map[3] +if(nn) +note.midi=pit2mid(nn.pit+19,nn.acc)} +function parse_basic_note(line,ulen){var nd,note=parse_acc_pit(line) +if(!note) +return +if(line.char()=='0'){parse.stemless=true;line.index++} +nd=parse_dur(line);note.dur=ulen*nd[0]/nd[1] +return note} +function parse_vpos(){var line=parse.line,ty=0 +if(a_dcn.length&&a_dcn[a_dcn.length-1]=="dot"){ty=C.SL_DOTTED +a_dcn.pop()} +switch(line.next_char()){case"'":line.index++ +return ty+C.SL_ABOVE +case",":line.index++ +return ty+C.SL_BELOW +case'?':line.index++ +return ty+C.SL_CENTER} +return ty+C.SL_AUTO} +function slur_add(s,nt){var i,s2,sl +for(i=curvoice.sls.length;--i>=0;){sl=curvoice.sls[i] +if(sl.ss==s) +continue +curvoice.sls.splice(i,1) +sl.se=s +if(nt) +sl.nte=nt +s2=sl.ss +if(!s2.sls) +s2.sls=[] +s2.sls.push(sl) +if(sl.grace) +sl.grace.sl1=true +return} +for(s2=s.prev;s2;s2=s2.prev){if(s2.type==C.BAR&&s2.bar_type[0]==':'&&s2.text){if(!s2.sls) +s2.sls=[];s2.sls.push({ty:C.SL_AUTO,ss:s2,se:s}) +if(nt) +s2.sls[s2.sls.length-1].nte=nt +return}} +if(!s.sls) +s.sls=[];s.sls.push({ty:C.SL_AUTO,se:s,loc:'i'}) +if(nt) +s.sls[s.sls.length-1].nte=nt} +function pit2mid(pit,acc){var p=[0,2,4,5,7,9,11][pit%7],o=((pit/7)|0)*12,p0,p1,s,b40 +if(curvoice.snd_oct) +o+=curvoice.snd_oct +if(acc==3) +acc=0 +if(acc){if(typeof acc=="object"){s=acc[0]/acc[1] +if(acc[1]==100) +return p+o+s}else{s=acc}}else{if(cfmt.temper) +return cfmt.temper[abc2svg.p_b40[pit%7]]+o +return p+o} +if(!cfmt.nedo){if(!cfmt.temper){p+=o+s +return p}}else{if(typeof acc!="object"){b40=abc2svg.p_b40[pit%7]+acc +return cfmt.temper[b40]+o} +if(acc[1]==cfmt.nedo){b40=abc2svg.p_b40[pit%7] +return cfmt.temper[b40]+o+s}} +p0=cfmt.temper[abc2svg.p_b40[pit%7]] +if(s>0){p1=cfmt.temper[(abc2svg.p_b40[pit%7]+1)%40] +if(p1p0) +p1-=12 +s=-s} +return p0+o+(p1-p0)*s} +function do_ties(s,tie_s){var i,m,not1,not2,mid,g,nt=0,se=(tie_s.time+tie_s.dur)==curvoice.time +for(m=0;m<=s.nhd;m++){not2=s.notes[m] +mid=not2.midi +if(tie_s.type!=C.GRACE){for(i=0;i<=tie_s.nhd;i++){not1=tie_s.notes[i] +if(!not1.tie_ty) +continue +if(not1.midi==mid&&(!se||!not1.tie_e)){not2.tie_s=not1 +not2.s=s +if(se){not1.tie_e=not2 +not1.s=tie_s} +nt++ +break}}}else{for(g=tie_s.extra;g;g=g.next){not1=g.notes[0] +if(!not1.tie_ty) +continue +if(not1.midi==mid){g.ti1=true +not2.tie_s=not1 +not2.s=s +not1.tie_e=not2 +not1.s=g +nt++ +break}}}} +if(!nt) +error(1,tie_s,"Bad tie") +else +s.ti2=true} +Abc.prototype.new_note=function(grace,sls){var note,s,in_chord,c,dcn,type,tie_s,acc_tie,i,n,s2,nd,res,num,dur,apit,div,ty,dpit=0,sl1=[],line=parse.line,a_dcn_sav=a_dcn +a_dcn=[] +parse.stemless=false;s={type:C.NOTE,fname:parse.fname,stem:0,multi:0,nhd:0,xmx:0} +s.istart=parse.bol+line.index +if(curvoice.color) +s.color=curvoice.color +if(grace){s.grace=true}else{if(curvoice.tie_s){tie_s=curvoice.tie_s +curvoice.tie_s=null} +if(a_gch) +csan_add(s) +if(parse.repeat_n){s.repeat_n=parse.repeat_n;s.repeat_k=parse.repeat_k;parse.repeat_n=0}} +c=line.char() +switch(c){case'X':s.invis=true +case'Z':s.type=C.MREST;c=line.next_char() +s.nmes=(c>'0'&&c<='9')?line.get_int():1;if(curvoice.wmeasure==1){error(1,s,"multi-measure rest, but no measure!") +return} +s.dur=curvoice.wmeasure*s.nmes +if(curvoice.second){delete curvoice.eoln +curvoice.time+=s.dur +return} +if(s.nmes==1){s.type=C.REST;s.dur_orig=s.dur;s.fmr=1 +s.notes=[{pit:18,dur:s.dur}]}else{glovar.mrest_p=true +if(par_sy.voices.length==1){s.tacet=curvoice.tacet +delete s.invis}} +break +case'y':s.type=C.SPACE;s.invis=true;s.dur=0;c=line.next_char() +if(c>='0'&&c<='9') +s.width=line.get_int() +else +s.width=10 +if(tie_s){curvoice.tie_s=tie_s +tie_s=null} +break +case'x':s.invis=true +case'z':s.type=C.REST;line.index++;nd=parse_dur(line);s.dur_orig=((curvoice.ulen<0)?C.BLEN:curvoice.ulen)*nd[0]/nd[1];s.dur=s.dur_orig*curvoice.dur_fact;if(s.dur==curvoice.wmeasure) +s.fmr=1 +s.notes=[{pit:18,dur:s.dur_orig}] +break +case'[':in_chord=true;c=line.next_char() +default:if(curvoice.acc_tie){acc_tie=curvoice.acc_tie +curvoice.acc_tie=null} +s.notes=[] +while(1){if(in_chord){while(1){if(!c) +break +i=c.charCodeAt(0);if(i>=128){syntax(1,errs.not_ascii) +return} +type=char_tb[i] +switch(type[0]){case'(':sl1.push(parse_vpos());c=line.char() +continue +case'!':if(type.length>1) +a_dcn.push(type.slice(1,-1)) +else +get_deco() +c=line.next_char() +continue} +break}} +note=parse_basic_note(line,s.grace?C.BLEN/4:curvoice.ulen<0?C.BLEN:curvoice.ulen) +if(!note) +return +if(curvoice.octave) +note.pit+=curvoice.octave*7 +apit=note.pit+19 +i=note.acc +if(!i){if(cfmt["propagate-accidentals"][0]=='p') +i=curvoice.acc[apit%7] +else +i=curvoice.acc[apit] +if(!i) +i=curvoice.ckey.k_map[apit%7]||0} +if(i){if(cfmt["propagate-accidentals"][0]=='p') +curvoice.acc[apit%7]=i +else if(cfmt["propagate-accidentals"][0]!='n') +curvoice.acc[apit]=i} +if(acc_tie&&acc_tie[apit]) +i=acc_tie[apit] +if(curvoice.map&&maps[curvoice.map]) +set_map(note,i) +if(!note.midi) +note.midi=pit2mid(apit,i) +if(curvoice.tr_sco&&!note.notrp){i=nt_trans(note,i) +if(i==-3){error(1,s,"triple sharp/flat") +i=note.acc>0?1:-1 +note.pit+=i +note.acc=i} +dpit=note.pit+19-apit} +if(curvoice.tr_snd) +note.midi+=curvoice.tr_snd +if(i){switch(cfmt["writeout-accidentals"][1]){case'd':s2=curvoice.ckey +if(!s2.k_a_acc) +break +for(n=0;n0){n=num*2-1;s.dur=s.dur*n/num;s.dur_orig=s.dur_orig*n/num +for(i=0;i<=s.nhd;i++) +s.notes[i].dur=s.notes[i].dur*n/num;s2.dur/=num;s2.dur_orig/=num +for(i=0;i<=s2.nhd;i++) +s2.notes[i].dur/=num}else{num=-num;n=num*2-1;s.dur/=num;s.dur_orig/=num +for(i=0;i<=s.nhd;i++) +s.notes[i].dur/=num;s2.dur=s2.dur*n/num;s2.dur_orig=s2.dur_orig*n/num +for(i=0;i<=s2.nhd;i++) +s2.notes[i].dur=s2.notes[i].dur*n/num} +curvoice.time=s2.time+s2.dur;for(s2=s2.next;s2;s2=s2.next) +s2.time=curvoice.time}}else{div=curvoice.ckey.k_bagpipe?8:4 +for(i=0;i<=s.nhd;i++) +s.notes[i].dur/=div;s.dur/=div;s.dur_orig/=div +if(grace.stem) +s.stem=grace.stem} +curvoice.last_note=s +c=line.char() +while(1){switch(c){case'.':if(line.buffer[line.index+1]!='-') +break +a_dcn.push("dot") +line.index++ +case'-':ty=parse_vpos() +for(i=0;i<=s.nhd;i++){s.notes[i].tie_ty=ty +s.notes[i].s=s} +curvoice.tie_s=grace||s +curvoice.tie_s.ti1=true +for(i=0;i<=s.nhd;i++){note=s.notes[i] +apit=note.pit+19 +-dpit +if(curvoice.acc[apit]||(acc_tie&&acc_tie[apit])){if(!curvoice.acc_tie) +curvoice.acc_tie=[] +n=curvoice.acc[apit] +if(acc_tie&&acc_tie[apit]) +n=acc_tie[apit] +curvoice.acc_tie[apit]=n}} +c=line.char() +continue} +break} +if(tie_s) +do_ties(s,tie_s)} +sym_link(s) +if(!grace){if(!curvoice.lyric_restart) +curvoice.lyric_restart=s +if(!curvoice.sym_restart) +curvoice.sym_restart=s} +if(a_dcn_sav.length){a_dcn=a_dcn_sav +deco_cnv(s,s.prev)} +if(grace&&s.ottava) +grace.ottava=s.ottava +if(parse.stemless) +s.stemless=true +s.iend=parse.bol+line.index +return s} +function tp_adj(s,fact){var d,tim=s.time,to=curvoice.time-tim,tt=to*fact +curvoice.time=tim+tt +while(1){s.in_tuplet=true +if(!s.grace){s.time=tim +if(s.dur){d=Math.round(s.dur*tt/to) +to-=s.dur +s.dur=d +tt-=s.dur +tim+=s.dur}} +if(!s.next){if(s.tpe) +s.tpe++ +else +s.tpe=1 +break} +s=s.next}} +function get_deco(){var c,line=parse.line,i=line.index,dcn="" +while(1){c=line.next_char() +if(!c){line.index=i +syntax(1,"No end of decoration") +return} +if(c=='!') +break +dcn+=c} +a_dcn.push(dcn)} +var nil="0",char_tb=[nil,nil,nil,nil,nil,nil,nil,nil,nil," ","\n",nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil," ","!",'"',"i","\n",nil,"&",nil,"(",")","i",nil,nil,"-","!dot!",nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,"|","i","<","n","<","i","i","n","n","n","n","n","n","n","!fermata!","d","d","d","!emphasis!","!lowermordent!","d","!coda!","!uppermordent!","d","d","!segno!","!trill!","d","d","d","n","d","n","[","\\","|","n","n","i","n","n","n","n","n","n","n","d","d","d","d","d","d","d","d","d","d","d","d","d","!upbow!","!downbow!","d","n","n","n","{","|","}","!gmark!",nil,] +function parse_music_line(){var grace,last_note_sav,a_dcn_sav,no_eol,s,tps,tp=[],tpn=-1,sls=[],line=parse.line +function check_mac(m){var i,j,b +for(i=1,j=line.index+1;i=14){n-=7;c+="'"} +return ntb[n]+c} +function expand(m,b){if(b==undefined) +return m +var c,i,r="",n=m.length +for(i=0;i='h'&&c<='z'){r+=n2n(b+c.charCodeAt(0)-'n'.charCodeAt(0))}else{r+=c}} +return r} +function parse_mac(k,m,b){var te,ti,curv,s,line_sav=line,istart_sav=parse.istart;parse.line=line=new scanBuf;parse.istart+=line_sav.index;if(cfmt.writefields.indexOf('m')<0){line.buffer=k.replace('n',n2n(b)) +s=curvoice.last_sym +ti=curvoice.time +parse_seq(true) +if(!s) +s=curvoice.sym +for(s=s.next;s;s=s.next) +s.noplay=true +te=curvoice.time +curv=curvoice +curvoice=clone_voice(curv.id+'-p') +if(!par_sy.voices[curvoice.v]){curvoice.second=true +par_sy.voices[curvoice.v]={st:curv.st,second:true,range:curvoice.v}} +curvoice.time=ti +s=curvoice.last_sym +parse.line=line=new scanBuf +parse.istart+=line_sav.index +line.buffer=expand(m,b) +parse_seq(true) +if(curvoice.time!=te) +syntax(1,"Bad length of the macro sequence") +if(!s) +s=curvoice.sym +for(;s;s=s.next) +s.invis=s.play=true +curvoice=curv}else{line.buffer=expand(m,b) +parse_seq(true)} +parse.line=line=line_sav +parse.istart=istart_sav} +function parse_seq(in_mac){var c,idx,type,k,s,dcn,i,n,text,note +while(1){c=line.char() +if(!c) +break +if(!in_mac&&maci[c]){n=undefined +for(k in mac){if(!mac.hasOwnProperty(k)||k[0]!=c) +continue +if(k.indexOf('n')<0){if(line.buffer.indexOf(k,line.index) +!=line.index) +continue +line.index+=k.length}else{n=check_mac(k) +if(n==undefined) +continue} +parse_mac(k,mac[k],n) +n=1 +break} +if(n) +continue} +idx=c.charCodeAt(0) +if(idx>=128){syntax(1,errs.not_ascii) +line.index++ +break} +type=char_tb[idx] +switch(type[0]){case' ':s=curvoice.last_note +if(s){s.beam_end=true +if(grace) +grace.gr_shift=true} +break +case'\n':if(cfmt.barsperstaff) +break +curvoice.eoln=true +break +case'&':if(grace){syntax(1,errs.bad_grace) +break} +c=line.next_char() +if(c==')'){get_vover(c) +break} +get_vover('&') +continue +case'(':c=line.next_char() +if(c>'0'&&c<='9'){if(grace){syntax(1,errs.bad_grace) +break} +var pplet=line.get_int(),qplet=qplet_tb[pplet],rplet=pplet +c=line.char() +if(c==':'){c=line.next_char() +if(c>'0'&&c<='9'){qplet=line.get_int();c=line.char()} +if(c==':'){c=line.next_char() +if(c>'0'&&c<='9'){rplet=line.get_int();c=line.char()}else{syntax(1,"Invalid 'r' in tuplet") +continue}}} +if(qplet==0||qplet==undefined) +qplet=(curvoice.wmeasure%9)==0?3:2;if(tpn<0) +tpn=tp.length +tp.push({p:pplet,q:qplet,r:rplet,ro:rplet,f:curvoice.tup||cfmt.tuplets}) +continue} +if(c=='&'){if(grace){syntax(1,errs.bad_grace) +break} +get_vover('(') +break} +line.index--;sls.push(parse_vpos()) +continue +case')':s=curvoice.last_sym +if(s){switch(s.type){case C.SPACE:if(!s.notes){s.notes=[] +s.notes[0]={}} +case C.NOTE:case C.REST:break +case C.GRACE:for(s=s.extra;s.next;s=s.next);break +default:s=null +break}} +if(!s){syntax(1,errs.bad_char,c) +break} +slur_add(s) +break +case'!':if(type.length>1) +a_dcn.push(type.slice(1,-1)) +else +get_deco() +break +case'"':if(grace){syntax(1,errs.bad_grace) +break} +parse_gchord(type) +break +case'[':if(type.length>1){self.do_pscom(type.slice(3,-1)) +break} +var c_next=line.buffer[line.index+1] +if('|[]: "'.indexOf(c_next)>=0||(c_next>='1'&&c_next<='9')){if(grace){syntax(1,errs.bar_grace) +break} +new_bar() +continue} +if(line.buffer[line.index+2]==':'){if(grace){syntax(1,errs.bad_grace) +break} +i=line.buffer.indexOf(']',line.index+1) +if(i<0){syntax(1,"Lack of ']'") +break} +text=line.buffer.slice(line.index+3,i).trim() +parse.istart=parse.bol+line.index;parse.iend=parse.bol+ ++i;line.index=0;do_info(c_next,text);line.index=i +continue} +case'n':s=self.new_note(grace,sls) +if(!s) +continue +if(grace||!s.notes) +continue +if(tpn>=0){s.tp=tp.slice(tpn) +tpn=-1 +if(tps) +s.tp[0].s=tps +tps=s}else if(!tps){continue} +k=tp[tp.length-1] +if(--k.r>0) +continue +while(1){tp_adj(tps,k.q/k.p) +i=k.ro +if(k.s) +tps=k.s +tp.pop() +if(!tp.length){tps=null +break} +k=tp[tp.length-1] +k.r-=i +if(k.r>0) +break} +continue +case'<':if(!curvoice.last_note){syntax(1,"No note before '<'") +break} +if(grace){syntax(1,"Cannot have a broken rhythm in grace notes") +break} +n=c=='<'?1:-1 +while(c=='<'||c=='>'){n*=2;c=line.next_char()} +curvoice.brk_rhythm=n +continue +case'i':break +case'{':if(grace){syntax(1,"'{' in grace note") +break} +last_note_sav=curvoice.last_note;curvoice.last_note=null;a_dcn_sav=a_dcn;a_dcn=[] +grace={type:C.GRACE,fname:parse.fname,istart:parse.bol+line.index,dur:0,multi:0} +if(curvoice.color) +grace.color=curvoice.color +switch(curvoice.pos.gst&0x07){case C.SL_ABOVE:grace.stem=1;break +case C.SL_BELOW:grace.stem=-1;break +case C.SL_HIDDEN:grace.stem=2;break} +sym_link(grace);c=line.next_char() +if(c=='/'){grace.sappo=true +break} +continue +case'|':if(grace){syntax(1,errs.bar_grace) +break} +new_bar() +continue +case'}':if(curvoice.ignore){grace=null +break} +s=curvoice.last_note +if(!grace||!s){syntax(1,errs.bad_char,c) +break} +if(a_dcn.length) +syntax(1,"Decoration ignored");grace.extra=grace.next;grace.extra.prev=null;grace.next=null;curvoice.last_sym=grace;grace=null +if(!s.prev&&!curvoice.ckey.k_bagpipe){for(i=0;i<=s.nhd;i++) +s.notes[i].dur*=2;s.dur*=2;s.dur_orig*=2} +curvoice.last_note=last_note_sav;a_dcn=a_dcn_sav +break +case"\\":if(!line.buffer[line.index+1]){no_eol=true +break} +default:syntax(1,errs.bad_char,c) +break} +line.index++}} +if(parse.state!=3) +return +if(parse.tp){tp=parse.tp +tpn=parse.tpn +tps=parse.tps +parse.tp=null} +parse_seq() +if(tp.length){parse.tp=tp +parse.tps=tps +parse.tpn=tpn} +if(sls.length) +syntax(1,"Start of slur without note") +if(grace){syntax(1,"No end of grace note sequence");curvoice.last_sym=grace.prev;curvoice.last_note=last_note_sav +if(grace.prev) +grace.prev.next=null} +if(!no_eol&&!cfmt.barsperstaff&&!vover&&char_tb['\n'.charCodeAt(0)]=='\n') +curvoice.eoln=true +if(curvoice.eoln&&cfmt.breakoneoln&&curvoice.last_note) +curvoice.last_note.beam_end=true} +var sheet +var add_fstyle=typeof document!="undefined"?function(s){var e +if(cfmt.fullsvg) +font_style+="\n"+s +if(!sheet){if(abc2svg.sheet){sheet=abc2svg.sheet +e=sheet.cssRules.length +while(--e>=0) +sheet.deleteRule(e)}else{e=document.createElement('style') +document.head.appendChild(e) +abc2svg.sheet=sheet=e.sheet}} +s=s.match(/[^{]+{[^}]+}/g) +while(1){e=s.shift() +if(!e) +break +sheet.insertRule(e,sheet.cssRules.length)}}:function(s){font_style+="\n"+s} +var +sw_tb=new Float32Array([.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.250,.333,.408,.500,.500,.833,.778,.333,.333,.333,.500,.564,.250,.564,.250,.278,.500,.500,.500,.500,.500,.500,.500,.500,.500,.500,.278,.278,.564,.564,.564,.444,.921,.722,.667,.667,.722,.611,.556,.722,.722,.333,.389,.722,.611,.889,.722,.722,.556,.722,.667,.556,.611,.722,.722,.944,.722,.722,.611,.333,.278,.333,.469,.500,.333,.444,.500,.444,.500,.444,.333,.500,.500,.278,.278,.500,.278,.778,.500,.500,.500,.500,.333,.389,.278,.500,.500,.722,.500,.500,.444,.480,.200,.480,.541,.500]),ssw_tb=new Float32Array([.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.000,.278,.278,.355,.556,.556,.889,.667,.191,.333,.333,.389,.584,.278,.333,.278,.278,.556,.556,.556,.556,.556,.556,.556,.556,.556,.556,.278,.278,.584,.584,.584,.556,1.015,.667,.667,.722,.722,.667,.611,.778,.722,.278,.500,.667,.556,.833,.722,.778,.667,.778,.722,.667,.611,.722,.667,.944,.667,.667,.611,.278,.278,.278,.469,.556,.333,.556,.556,.500,.556,.556,.278,.556,.556,.222,.222,.500,.222,.833,.556,.556,.556,.556,.333,.500,.278,.556,.500,.722,.500,.500,.500,.334,.260,.334,.584,.512]),mw_tb=new Float32Array([.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.0,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52,.52]) +function cwid(c,font){var i=c.charCodeAt(0) +if(i>=0x80){if(i>=0x300&&i<0x370) +return 0;i=0x61} +return(font||gene.curfont).cw_tb[i]} +function cwidf(c){return cwid(c)*gene.curfont.swfac} +var strwh +(function(){if(typeof document!="undefined"){var el +strwh=function(str){if(str.wh) +return str.wh +if(!el){el=document.createElement('text') +el.style.position='absolute' +el.style.top='-1000px' +el.style.padding='0' +el.style.visibility="hidden" +document.body.appendChild(el)} +var c,font=gene.curfont,h=font.size,w=0,n=str.length,i0=0,i=0 +el.className=font_class(font) +el.style.lineHeight=1 +if(typeof str=="object"){el.innerHTML=str +str.wh=[el.clientWidth,el.clientHeight] +return str.wh} +str=str.replace(/<|>|&[^&\s]*?;|&/g,function(c){switch(c){case'<':return"<" +case'>':return">" +case'&':return"&"} +return c}) +while(1){i=str.indexOf('$',i) +if(i>=0){c=str[i+1] +if(c=='0'){font=gene.deffont}else if(c>='1'&&c<='9'){font=get_font("u"+c)}else{i++ +continue}} +el.innerHTML=str.slice(i0,i>=0?i:undefined) +w+=el.clientWidth +if(el.clientHeight>h) +h=el.clientHeight +if(i<0) +break +el.style.font=style_font(font).slice(5);i+=2;i0=i} +return[w,h]}}else{strwh=function(str){var font=gene.curfont,swfac=font.swfac,h=font.size,w=0,i,j,c,n=str.length +for(i=0;i='1'&&c<='9'){font=get_font("u"+c)}else{c='$' +break} +i++;swfac=font.swfac +if(font.size>h) +h=font.size +continue +case'&':if(str[i+1]==' ') +break +j=str.indexOf(';',i) +if(j>0&&j-i<10){i=j;c='a'} +break} +w+=cwid(c,font)*swfac} +return[w,h]}}})() +function str2svg(str){if(typeof str=="object") +return str +var n_font,wh,o_font=gene.deffont,c_font=gene.curfont,o="" +function tspan(nf,of){var cl +if(nf.class&&nf.name==of.name&&nf.size==of.size&&nf.weight==of.weight&&nf.style==of.style) +cl=nf.class +else +cl=font_class(nf) +return''} +if(c_font!=o_font) +o=tspan(c_font,o_font) +o+=str.replace(/<|>|&[^&\s]*?;|&|\$./g,function(c){switch(c){case'<':return"<" +case'>':return">" +case'&':return"&" +default:if(c[0]!='$') +break +if(c[1]=='0') +n_font=gene.deffont +else if(c[1]>='1'&&c[1]<='9') +n_font=get_font("u"+c[1]) +else +break +c='' +if(n_font==c_font) +return c +if(c_font!=o_font) +c="" +c_font=n_font +if(c_font==o_font) +return c +return c+tspan(c_font,o_font)} +return c}) +if(c_font!=o_font) +o+="" +o=new String(o) +if(typeof document!="undefined") +strwh(o) +else +o.wh=strwh(str) +gene.curfont=c_font +return o} +function set_font(xxx){if(typeof xxx=="string") +xxx=get_font(xxx) +gene.curfont=gene.deffont=xxx} +function out_str(str){output+=str2svg(str)} +function xy_str(x,y,str,action,w,wh){if(!wh) +wh=str.wh||strwh(str) +if(cfmt.trimsvg){var wx=wh[0] +switch(action){case'c':wx=wh[0]/2 +break +case'j':wx=w +break +case'r':wx=0 +break} +if(img.wx5&&gene.deffont.wadj) +output+='" lengthAdjust="'+gene.deffont.wadj+'" textLength="'+wh[0].toFixed(1);output+='" x="';out_sxsy(x,'" y="',y) +switch(action){case'c':output+='" text-anchor="middle">' +break +case'j':output+='" textLength="'+w.toFixed(1)+'">' +break +case'r':output+='" text-anchor="end">' +break +default:output+='">' +break} +out_str(str);output+="\n"} +function trim_title(title,is_subtitle){var i +if(cfmt.titletrim){i=title.lastIndexOf(", ") +if(i<0||title[i+2]<'A'||title[i+2]>'Z'){i=0}else if(cfmt.titletrim==1){if(i=0) +i=0}else{if(i=0) +title=info.X+'. '+title +if(cfmt.titlecaps) +return title.toUpperCase() +return title} +function get_lwidth(){if(img.chg) +set_page() +return(img.width-img.lm-img.rm +-2) +/ cfmt.scale} +function write_title(title,is_subtitle){var h,wh +if(!title) +return +set_page();title=trim_title(title,is_subtitle) +if(is_subtitle){set_font("subtitle");h=cfmt.subtitlespace}else{set_font("title");h=cfmt.titlespace} +wh=strwh(title) +wh[1]+=gene.curfont.pad*2 +vskip(wh[1]+h+gene.curfont.pad) +h=gene.curfont.pad+wh[1]*.22 +if(cfmt.titleleft) +xy_str(0,h,title,null,null,wh) +else +xy_str(get_lwidth()/2,h,title,"c",null,wh)} +function put_inf2r(x,y,str1,str2,action){if(!str1){if(!str2) +return +str1=str2;str2=null} +if(!str2) +xy_str(x,y,str1,action) +else +xy_str(x,y,str1+' ('+str2+')',action)} +function write_text(text,action){if(action=='s') +return +set_page();var wh,font,o,strlw=get_lwidth(),sz=gene.curfont.size,lineskip=sz*cfmt.lineskipfac,parskip=sz*cfmt.parskipfac,i,j,x,words,w,k,ww,str;switch(action){default:font=gene.curfont +switch(action){case'c':x=strlw/2;break +case'r':x=strlw-font.pad;break +default:x=font.pad;break} +j=0 +while(1){i=text.indexOf('\n',j) +if(i==j){vskip(parskip);blk_flush() +use_font(gene.curfont) +while(text[i+1]=='\n'){vskip(lineskip);i++} +if(i==text.length) +break}else{if(i<0) +str=text.slice(j) +else +str=text.slice(j,i) +ww=strwh(str) +vskip(ww[1]*cfmt.lineskipfac ++font.pad*2) +xy_str(x,font.pad+ww[1]*.2,str,action) +if(i<0) +break} +j=i+1} +vskip(parskip);blk_flush() +break +case'f':case'j':j=0 +while(1){i=text.indexOf('\n\n',j) +if(i<0) +words=text.slice(j) +else +words=text.slice(j,i);words=words.split(/\s+/);w=k=wh=0 +for(j=0;j=strlw){vskip(wh*cfmt.lineskipfac) +xy_str(0,ww[1]*.2,words.slice(k,j).join(' '),action,strlw) +k=j;w=ww[0] +wh=0} +if(ww[1]>wh) +wh=ww[1]} +if(w!=0){vskip(wh*cfmt.lineskipfac) +xy_str(0,ww[1]*.2,words.slice(k).join(' '))} +vskip(parskip);blk_flush() +if(i<0) +break +while(text[i+2]=='\n'){vskip(lineskip);i++} +if(i==text.length) +break +use_font(gene.curfont);j=i+2} +break}} +function put_words(words){var p,i,j,nw,w,lw,x1,x2,i1,i2,do_flush,maxn=0,n=1 +function put_wline(p,x){var i=0,k=0 +if(p[0]=='$'&&p[1]>='0'&&p[1]<='9'){gene.curfont=p[1]=='0'?gene.deffont:get_font("u"+p[1]) +p=p.slice(2)} +if((p[i]>='0'&&p[i]<='9')||p[i+1]=='.'){while(imaxn){maxn=p.length +i1=i}} +w=get_lwidth()/2 +lw=strwh(words[i1])[0] +i1=i2=0 +if(lw>1 +for(i=0;i>=1} +if(i2){x1=(w-lw)/2+10 +x2=x1+w}else{x2=w-lw/2+10} +do_flush=true +for(i=0;i'Z'){switch(c){case'+':align='+' +c=p[i++] +break +case',':fmt+='\n' +default:continue +case'<':align='l' +c=p[i++] +break +case'>':align='r' +c=p[i++] +break}}else{switch(p[i]){case'-':align='l' +i++ +break +case'1':align='r' +i++ +break +case'0':i++ +default:align='c' +break}} +if(!info_val[c]){if(!info[c]) +continue +info_val[c]=info[c].split('\n');info_nb[c]=1}else{info_nb[c]++} +fmt+=align+c} +fmt+='\n' +var ya={l:cfmt.titlespace,c:cfmt.titlespace,r:cfmt.titlespace},xa={l:0,c:lwidth*.5,r:lwidth},yb={},str;p=fmt;i=0 +while(1){yb.l=yb.c=yb.r=y=0;j=i +while(1){align=p[j++] +if(align=='\n') +break +c=p[j++] +if(align=='+'||yb[align]) +continue +str=info_val[c] +if(!str) +continue +font_name=info_font[c] +if(!font_name) +font_name="history";font=get_font(font_name);sz=font.size*1.1 +if(info_sz[c]) +sz+=info_sz[c] +if(y0){y+=sz;str=info_val[c].shift();xy_str(x,-y,str,align)}} +info_nb[c]--;ya[align]=y} +if(ya.c>ya.l) +ya.l=ya.c +if(ya.r>ya.l) +ya.l=ya.r +if(i>=p.length) +break +ya.c=ya.r=ya.l} +vskip(ya.l)} +function write_heading(){var i,j,area,composer,origin,rhythm,down1,down2,lwidth=get_lwidth() +vskip(cfmt.topspace) +if(cfmt.titleformat){write_headform(lwidth);vskip(cfmt.musicspace) +return} +if(info.T&&cfmt.writefields.indexOf('T')>=0){i=0 +while(1){j=info.T.indexOf("\n",i) +if(j<0){write_title(info.T.substring(i),i!=0) +break} +write_title(info.T.slice(i,j),i!=0);i=j+1}} +down1=down2=0 +if(parse.ckey.k_bagpipe&&!cfmt.infoline&&cfmt.writefields.indexOf('R')>=0) +rhythm=info.R +if(rhythm){set_font("composer");down1=cfmt.composerspace+gene.curfont.size+2 +xy_str(0,-down1+gene.curfont.size*.22,rhythm)} +area=info.A +if(cfmt.writefields.indexOf('C')>=0) +composer=info.C +if(cfmt.writefields.indexOf('O')>=0) +origin=info.O +if(composer||origin||cfmt.infoline){var xcomp,align;set_font("composer");if(cfmt.aligncomposer<0){xcomp=0;align=' '}else if(cfmt.aligncomposer==0){xcomp=lwidth*.5;align='c'}else{xcomp=lwidth;align='r'} +if(composer||origin){down2=cfmt.composerspace+2 +i=0 +while(1){down2+=gene.curfont.size +if(composer) +j=composer.indexOf("\n",i) +else +j=-1 +if(j<0){put_inf2r(xcomp,-down2+gene.curfont.size*.22,composer?composer.substring(i):null,origin,align) +break} +xy_str(xcomp,-down2+gene.curfont.size*.22,composer.slice(i,j),align);i=j+1}} +rhythm=rhythm?null:info.R +if((rhythm||area)&&cfmt.infoline){set_font("info");down2+=cfmt.infospace+gene.curfont.size +put_inf2r(lwidth,-down2+gene.curfont.size*.22,rhythm,area,'r')}} +if(info.P&&cfmt.writefields.indexOf('P')>=0){set_font("parts");i=cfmt.partsspace+gene.curfont.size+gene.curfont.pad +if(down1+i>down2) +down2=down1+i +else +down2+=i +xy_str(0,-down2+gene.curfont.size*.22,info.P) +down2+=gene.curfont.pad}else if(down1>down2){down2=down1} +vskip(down2+cfmt.musicspace)} +var output="",style='\ +\n.stroke{stroke:currentColor;fill:none}\ +\n.bW{stroke:currentColor;fill:none;stroke-width:1}\ +\n.bthW{stroke:currentColor;fill:none;stroke-width:3}\ +\n.slW{stroke:currentColor;fill:none;stroke-width:.7}\ +\n.slthW{stroke:currentColor;fill:none;stroke-width:1.5}\ +\n.sW{stroke:currentColor;fill:none;stroke-width:.7}\ +\n.box{outline:1px solid black;outline-offset:1px}',font_style='',posx=cfmt.leftmargin/cfmt.scale,posy=0,img={width:cfmt.pagewidth,lm:cfmt.leftmargin,rm:cfmt.rightmargin,wx:0,chg:1},defined_glyph={},defs='',fulldefs='',stv_g={scale:1,dy:0,st:-1,v:-1,g:0},blkdiv=0 +var tgls={"mtr ":{x:0,y:0,c:"\u0020"},brace:{x:0,y:0,c:"\ue000"},lphr:{x:0,y:23,c:"\ue030"},mphr:{x:0,y:23,c:"\ue038"},sphr:{x:0,y:26,c:"\ue039"},short:{x:0,y:32,c:"\ue038"},tick:{x:0,y:29,c:"\ue039"},rdots:{x:-1,y:0,c:"\ue043"},dsgn:{x:-12,y:0,c:"\ue045"},dcap:{x:-12,y:0,c:"\ue046"},sgno:{x:-5,y:0,c:"\ue047"},coda:{x:-10,y:0,c:"\ue048"},tclef:{x:-8,y:0,c:"\ue050"},cclef:{x:-8,y:0,c:"\ue05c"},bclef:{x:-8,y:0,c:"\ue062"},pclef:{x:-6,y:0,c:"\ue069"},spclef:{x:-6,y:0,c:"\ue069"},stclef:{x:-8,y:0,c:"\ue07a"},scclef:{x:-8,y:0,c:"\ue07b"},sbclef:{x:-7,y:0,c:"\ue07c"},oct:{x:0,y:2,c:"\ue07d"},oct2:{x:0,y:2,c:"\ue07e"},mtr0:{x:0,y:0,c:"\ue080"},mtr1:{x:0,y:0,c:"\ue081"},mtr2:{x:0,y:0,c:"\ue082"},mtr3:{x:0,y:0,c:"\ue083"},mtr4:{x:0,y:0,c:"\ue084"},mtr5:{x:0,y:0,c:"\ue085"},mtr6:{x:0,y:0,c:"\ue086"},mtr7:{x:0,y:0,c:"\ue087"},mtr8:{x:0,y:0,c:"\ue088"},mtr9:{x:0,y:0,c:"\ue089"},mtrC:{x:0,y:0,c:"\ue08a"},"mtr+":{x:0,y:0,c:"\ue08c"},"mtr(":{x:0,y:0,c:"\ue094"},"mtr)":{x:0,y:0,c:"\ue095"},HDD:{x:-7,y:0,c:"\ue0a0"},breve:{x:-7,y:0,c:"\ue0a1"},HD:{x:-5.2,y:0,c:"\ue0a2"},Hd:{x:-3.8,y:0,c:"\ue0a3"},hd:{x:-3.7,y:0,c:"\ue0a4"},ghd:{x:2,y:0,c:"\ue0a4",sc:.66},pshhd:{x:-3.7,y:0,c:"\ue0a9"},pfthd:{x:-3.7,y:0,c:"\ue0b3"},x:{x:-3.7,y:0,c:"\ue0a9"},"circle-x":{x:-3.7,y:0,c:"\ue0b3"},srep:{x:-5,y:0,c:"\ue101"},"dot+":{x:-5,y:0,sc:.7,c:"\ue101"},diamond:{x:-4,y:0,c:"\ue1b9"},triangle:{x:-4,y:0,c:"\ue1bb"},dot:{x:-1,y:0,c:"\ue1e7"},flu1:{x:-.3,y:0,c:"\ue240"},fld1:{x:-.3,y:0,c:"\ue241"},flu2:{x:-.3,y:0,c:"\ue242"},fld2:{x:-.3,y:0,c:"\ue243"},flu3:{x:-.3,y:3.5,c:"\ue244"},fld3:{x:-.3,y:-4,c:"\ue245"},flu4:{x:-.3,y:8,c:"\ue246"},fld4:{x:-.3,y:-9,c:"\ue247"},flu5:{x:-.3,y:12.5,c:"\ue248"},fld5:{x:-.3,y:-14,c:"\ue249"},"acc-1":{x:-1,y:0,c:"\ue260"},"cacc-1":{x:-18,y:0,c:"\ue26a\ue260\ue26b"},"sacc-1":{x:-1,y:0,sc:.7,c:"\ue260"},acc3:{x:-1,y:0,c:"\ue261"},"cacc3":{x:-18,y:0,c:"\ue26a\ue261\ue26b"},sacc3:{x:-1,y:0,sc:.7,c:"\ue261"},acc1:{x:-2,y:0,c:"\ue262"},"cacc1":{x:-18,y:0,c:"\ue26a\ue262\ue26b"},sacc1:{x:-2,y:0,sc:.7,c:"\ue262"},acc2:{x:-3,y:0,c:"\ue263"},"acc-2":{x:-3,y:0,c:"\ue264"},"acc-1_2":{x:-2,y:0,c:"\ue280"},"acc-3_2":{x:-3,y:0,c:"\ue281"},acc1_2:{x:-1,y:0,c:"\ue282"},acc3_2:{x:-3,y:0,c:"\ue283"},accent:{x:-3,y:2,c:"\ue4a0"},stc:{x:0,y:-2,c:"\ue4a2"},emb:{x:0,y:-2,c:"\ue4a4"},wedge:{x:0,y:0,c:"\ue4a8"},marcato:{x:-3,y:-2,c:"\ue4ac"},hld:{x:-7,y:-2,c:"\ue4c0"},brth:{x:0,y:0,c:"\ue4ce"},caes:{x:0,y:8,c:"\ue4d1"},r00:{x:-1.5,y:0,c:"\ue4e1"},r0:{x:-1.5,y:0,c:"\ue4e2"},r1:{x:-3.5,y:-6,c:"\ue4e3"},r2:{x:-3.2,y:0,c:"\ue4e4"},r4:{x:-3,y:0,c:"\ue4e5"},r8:{x:-3,y:0,c:"\ue4e6"},r16:{x:-4,y:0,c:"\ue4e7"},r32:{x:-4,y:0,c:"\ue4e8"},r64:{x:-4,y:0,c:"\ue4e9"},r128:{x:-4,y:0,c:"\ue4ea"},mrep:{x:-6,y:0,c:"\ue500"},mrep2:{x:-9,y:0,c:"\ue501"},p:{x:-3,y:0,c:"\ue520"},f:{x:-3,y:0,c:"\ue522"},pppp:{x:-15,y:0,c:"\ue529"},ppp:{x:-14,y:0,c:"\ue52a"},pp:{x:-8,y:0,c:"\ue52b"},mp:{x:-8,y:0,c:"\ue52c"},mf:{x:-8,y:0,c:"\ue52d"},ff:{x:-7,y:0,c:"\ue52f"},fff:{x:-10,y:0,c:"\ue530"},ffff:{x:-14,y:0,c:"\ue531"},sfz:{x:-10,y:0,c:"\ue539"},trl:{x:-5,y:-2,c:"\ue566"},turn:{x:-5,y:0,c:"\ue567"},turnx:{x:-5,y:0,c:"\ue569"},umrd:{x:-6,y:2,c:"\ue56c"},lmrd:{x:-6,y:2,c:"\ue56d"},dplus:{x:-3,y:0,c:"\ue582"},sld:{x:-8,y:12,c:"\ue5d0"},grm:{x:-3,y:-2,c:"\ue5e2"},dnb:{x:-3,y:0,c:"\ue610"},upb:{x:-2,y:0,c:"\ue612"},opend:{x:-2,y:-2,c:"\ue614"},roll:{x:0,y:0,c:"\ue618"},thumb:{x:-2,y:-2,c:"\ue624"},snap:{x:-2,y:-2,c:"\ue630"},ped:{x:-10,y:0,c:"\ue650"},pedoff:{x:-5,y:0,c:"\ue655"},mtro:{x:0,y:0,c:"\ue911"},mtrc:{x:0,y:0,c:"\ue915"},"mtr.":{x:0,y:0,c:"\ue920"},"mtr|":{x:0,y:0,c:"\ue925"},longa:{x:-4.7,y:0,c:"\ue95d"},custos:{x:-4,y:3,c:"\uea02"},ltr:{x:2,y:6,c:"\ueaa4"}} +var glyphs={} +function m_gl(s){return s.replace(/[Cco]\||[co]\.|./g,function(e){var m=tgls["mtr"+e] +return m?m.c:0})} +function def_use(gl){var i,j,g +if(defined_glyph[gl]) +return +defined_glyph[gl]=true;g=glyphs[gl] +if(!g){error(1,null,"Unknown glyph: '$1'",gl) +return} +j=0 +while(1){i=g.indexOf('xlink:href="#',j) +if(i<0) +break +i+=13;j=g.indexOf('"',i);def_use(g.slice(i,j))} +defs+='\n'+g} +function defs_add(text){var i,j,gl,tag,is,ie=0 +text=text.replace(//g,'') +while(1){is=text.indexOf('<',ie);if(is<0) +break +i=text.indexOf('id="',is) +if(i<0) +break +i+=4;j=text.indexOf('"',i);if(j<0) +break +gl=text.slice(i,j);ie=text.indexOf('>',j);if(ie<0) +break +if(text[ie-1]=='/'){ie++}else{i=text.indexOf(' ',is);if(i<0) +break +tag=text.slice(is+1,i);ie=text.indexOf('',ie) +if(ie<0) +break +ie+=3+tag.length} +if(text.substr(is,7)=='=0?staff_tb[st].staffscale:1 +if(st>=0&&new_scale!=1) +dy=staff_tb[st].y +else +dy=posy +if(new_scale==stv_g.scale&&dy==stv_g.dy) +return +stv_g.scale=new_scale;stv_g.dy=dy;stv_g.st=st;stv_g.v=-1;set_g()} +function set_scale(s){var new_dy,new_scale=s.p_v.scale +if(new_scale==1){set_sscale(s.st) +return} +new_dy=posy +if(staff_tb[s.st].staffscale!=1){new_scale*=staff_tb[s.st].staffscale;new_dy=staff_tb[s.st].y} +if(new_scale==stv_g.scale&&stv_g.dy==posy) +return +stv_g.scale=new_scale;stv_g.dy=new_dy;stv_g.st=staff_tb[s.st].staffscale==1?-1:s.st;stv_g.v=s.v;set_g()} +function set_dscale(st,no_scale){if(output){if(stv_g.started){stv_g.started=false +glout() +output+="\n"} +if(stv_g.st<0){staff_tb[0].output+=output}else if(stv_g.scale==1){staff_tb[stv_g.st].output+=output}else{staff_tb[stv_g.st].sc_out+=output} +output=""} +if(st<0) +stv_g.scale=1 +else +stv_g.scale=no_scale?1:staff_tb[st].staffscale;stv_g.st=st;stv_g.dy=0} +function delayed_update(){var st,new_out,text +for(st=0;st<=nstaff;st++){if(staff_tb[st].sc_out){output+='\n'+ +staff_tb[st].sc_out+'\n';staff_tb[st].sc_out=""} +if(!staff_tb[st].output) +continue +output+='\n'+ +staff_tb[st].output+'\n';staff_tb[st].output=""}} +function anno_out(s,t,f){if(s.istart==undefined) +return +var type=s.type,h=s.ymx-s.ymn+4,wl=s.wl||2,wr=s.wr||2 +if(s.grace) +type=C.GRACE +f(t||abc2svg.sym_name[type],s.istart,s.iend,s.x-wl-2,staff_tb[s.st].y+s.ymn+h-2,wl+wr+4,h,s)} +function a_start(s,t){anno_out(s,t,user.anno_start)} +function a_stop(s,t){anno_out(s,t,user.anno_stop)} +function empty_function(){} +var anno_start=empty_function,anno_stop=empty_function +function anno_put(){var s +while(1){s=anno_a.shift() +if(!s) +break +switch(s.type){case C.CLEF:case C.METER:case C.KEY:case C.REST:if(s.type!=C.REST||s.rep_nb){set_sscale(s.st) +break} +case C.GRACE:case C.NOTE:case C.MREST:set_scale(s) +break} +anno_stop(s)}} +function out_XYAB(str,x,y,a,b){x=sx(x);y=sy(y);output+=str.replace(/X|Y|A|B|F|G/g,function(c){switch(c){case'X':return x.toFixed(1) +case'Y':return y.toFixed(1) +case'A':return a +case'B':return b +case'F':return a.toFixed(1) +default:return b.toFixed(1)}})} +function g_open(x,y,rot,sx,sy){glout() +out_XYAB('\n';stv_g.g++} +function g_close(){glout() +stv_g.g--;output+='\n'} +Abc.prototype.out_svg=function(str){output+=str} +function sx(x){if(stv_g.g) +return x +return(x+posx)/stv_g.scale} +Abc.prototype.sx=sx +function sy(y){if(stv_g.g) +return-y +if(stv_g.scale==1) +return posy-y +if(stv_g.v>=0) +return(stv_g.dy-y)/voice_tb[stv_g.v].scale +return stv_g.dy-y} +Abc.prototype.sy=sy;Abc.prototype.sh=function(h){if(stv_g.st<0) +return h/stv_g.scale +return h} +Abc.prototype.ax=function(x){return x+posx} +Abc.prototype.ay=function(y){if(stv_g.st<0) +return posy-y +return posy+(stv_g.dy-y)*stv_g.scale-stv_g.dy} +Abc.prototype.ah=function(h){if(stv_g.st<0) +return h +return h*stv_g.scale} +function out_sxsy(x,sep,y){x=sx(x);y=sy(y);output+=x.toFixed(1)+sep+y.toFixed(1)} +Abc.prototype.out_sxsy=out_sxsy +function xypath(x,y,fill){if(fill) +out_XYAB('\n'}} +for(st=0;st<=nstaff;st++){p_st=staff_tb[st] +if(!p_st.hlu) +continue +set_sscale(st) +hlud(p_st.hlu,6) +hlud(p_st.hld,-6)}} +var gla=[[],[],"",[],[],[]] +function glout(){var e,v=[] +if(gla[0].length){while(1){e=gla[0].shift() +if(e==undefined) +break +v.push(e.toFixed(1))} +output+=''+gla[2]+'\n' +gla[2]=""} +if(!gla[3].length) +return +output+='\n'} +function xygl(x,y,gl){if(glyphs[gl]){def_use(gl) +out_XYAB('\n',x,y,gl)}else{var tgl=tgls[gl] +if(tgl){x+=tgl.x*stv_g.scale;y-=tgl.y +if(tgl.sc){out_XYAB('B\n',x,y,tgl.sc,tgl.c)}else{gla[0].push(sx(x)) +gla[1].push(sy(y)) +gla[2]+=tgl.c}}else{error(1,null,'no definition of $1',gl)}}} +function out_acciac(x,y,dx,dy,up){if(up){x-=1;y+=4}else{x-=5;y-=4} +out_XYAB('\n',x,y,dx,-dy)} +function out_brace(x,y,h){x+=posx-6;y=posy-y;h/=24;output+=''+tgls.brace.c+'\n'} +function out_bracket(x,y,h){x+=posx-5;y=posy-y-3;h+=2;output+='\n'} +function out_hyph(x,y,w){var n,a_y,d=25+((w/20)|0)*3 +if(w>15.) +n=((w-15)/d)|0 +else +n=0;x+=(w-d*n-5)/2;out_XYAB('\n',x,y+4,Math.round((d-5)/stv_g.scale),d*n+5)} +function out_stem(x,y,h,grace,nflags,straight){var dx=grace?GSTEM_XOFF:3.5,slen=-h +if(h<0) +dx=-dx;x+=dx*stv_g.scale +if(stv_g.v>=0) +slen/=voice_tb[stv_g.v].scale;gla[3].push(sx(x)) +gla[3].push(sy(y)) +gla[3].push(slen) +if(!nflags) +return +y+=h +if(h>0){if(!straight){if(!grace){xygl(x,y,"flu"+nflags) +return}else{output+='=0){out_XYAB('MX Yl7 3.2 0 3.2 -7 -3.2z\n',x,y);y-=5.4}}else{while(--nflags>=0){out_XYAB('MX Yl3 1.5 0 2 -3 -1.5z\n',x,y);y-=3}}}}else{if(!straight){if(!grace){xygl(x,y,"fld"+nflags) +return}else{output+='=0){out_XYAB('MX Yl7 -3.2 0 -3.2 -7 3.2z\n',x,y);y+=5.4}}}} +output+='"/>\n'} +function out_trem(x,y,ntrem){out_XYAB('\n'} +function out_tubr(x,y,dx,dy,up){var h=up?-3:3;y+=h;dx/=stv_g.scale;output+='\n'} +function out_tubrn(x,y,dx,dy,up,str){var dxx,sw=str.length*10,h=up?-3:3;set_font("tuplet") +xy_str(x+dx/2,y+dy/2-gene.curfont.size*.1,str,'c') +dx/=stv_g.scale +if(!up) +y+=6;output+='\n'+'\n'} +function out_wln(x,y,w){out_XYAB('\n',x,y+1,w)} +var deco_str_style={crdc:{dx:0,dy:5,style:'font:italic 14px text,serif',anchor:' text-anchor="middle"'},dacs:{dx:0,dy:3,style:'font:bold 15px text,serif',anchor:' text-anchor="middle"'},pf:{dx:0,dy:5,style:'font:italic bold 16px text,serif',anchor:' text-anchor="middle"'}} +deco_str_style.at=deco_str_style.crdc +function out_deco_str(x,y,de){var name=de.dd.glyph +if(name=='fng'){out_XYAB('\ +A\n',x-2,y,m_gl(de.dd.str)) +return} +if(name=='@'){name='at'}else if(!/^[A-Za-z][A-Za-z\-_]*$/.test(name)){error(1,de.s,"No function for decoration '$1'",de.dd.name) +return} +var f,a_deco=deco_str_style[name] +if(!a_deco) +a_deco=deco_str_style.crdc +else if(a_deco.style) +style+="\n."+name+"{"+a_deco.style+"}",delete a_deco.style +x+=a_deco.dx;y+=a_deco.dy;out_XYAB('',x,y,name,a_deco.anchor||"");set_font("annotation");out_str(de.dd.str) +output+='\n'} +function out_arp(x,y,val){g_open(x,y,270);x=0;val=Math.ceil(val/6) +while(--val>=0){xygl(x,6,"ltr");x+=6} +g_close()} +function out_cresc(x,y,val,defl){x+=val*stv_g.scale +val=-val;out_XYAB('\n' +else +output+='-4l'+(-val).toFixed(1)+' -4"/>\n'} +function out_dim(x,y,val,defl){out_XYAB('\n' +else +output+='-4l'+(-val).toFixed(1)+' -4"/>\n'} +function out_ltr(x,y,val){y+=4;val=Math.ceil(val/6) +while(--val>=0){xygl(x,y,"ltr");x+=6}} +Abc.prototype.out_lped=function(x,y,val,defl){if(!defl.nost) +xygl(x,y,"ped");if(!defl.noen) +xygl(x+val+6,y,"pedoff")} +function out_8va(x,y,val,defl){if(val<18){val=18 +x-=4} +if(!defl.nost){out_XYAB('8\ +va\n',x-8,y);x+=12;val-=12} +y+=6;out_XYAB('\n',x,y,val) +if(!defl.noen) +out_XYAB('\n',x+val,y)} +function out_8vb(x,y,val,defl){if(val<18){val=18 +x-=4} +if(!defl.nost){out_XYAB('8\ +vb\n',x-8,y);x+=10 +val-=10} +out_XYAB('\n',x,y,val) +if(!defl.noen) +out_XYAB('\n',x+val,y)} +function out_15ma(x,y,val,defl){if(val<25){val=25 +x-=6} +if(!defl.nost){out_XYAB('15\ +ma\n',x-10,y);x+=20;val-=20} +y+=6;out_XYAB('\n',x,y,val) +if(!defl.noen) +out_XYAB('\n',x+val,y)} +function out_15mb(x,y,val,defl){if(val<24){val=24 +x-=5} +if(!defl.nost){out_XYAB('15\ +mb\n',x-10,y);x+=18 +val-=18} +out_XYAB('\n',x,y,val) +if(!defl.noen) +out_XYAB('\n',x+val,y)} +var deco_val_tb={arp:out_arp,cresc:out_cresc,dim:out_dim,ltr:out_ltr,lped:function(x,y,val,defl){self.out_lped(x,y,val,defl)},"8va":out_8va,"8vb":out_8vb,"15ma":out_15ma,"15mb":out_15mb} +function out_deco_val(x,y,name,val,defl){if(deco_val_tb[name]) +deco_val_tb[name](x,y,val,defl) +else +error(1,null,"No function for decoration '$1'",name)} +function out_glisq(x2,y2,de){var ar,a,len,de1=de.start,x1=de1.x,y1=de1.y+staff_tb[de1.st].y,dx=x2-x1,dy=self.sh(y1-y2) +if(!stv_g.g) +dx/=stv_g.scale +ar=Math.atan2(dy,dx) +a=ar/Math.PI*180 +len=(dx-(de1.s.dots?13+de1.s.xmx:8) +-8-(de.s.notes[0].shac||0)) +/ Math.cos(ar) +g_open(x1,y1,a);x1=de1.s.dots?13+de1.s.xmx:8;len=len/6|0 +if(len<1) +len=1 +while(--len>=0){xygl(x1,0,"ltr");x1+=6} +g_close()} +function out_gliss(x2,y2,de){var ar,a,len,de1=de.start,x1=de1.x,y1=de1.y+staff_tb[de1.st].y,dx=x2-x1,dy=self.sh(y1-y2) +if(!stv_g.g) +dx/=stv_g.scale +ar=Math.atan2(dy,dx) +a=ar/Math.PI*180 +len=(dx-(de1.s.dots?13+de1.s.xmx:8) +-8-(de.s.notes[0].shac||0)) +/ Math.cos(ar) +g_open(x1,y1,a);xypath(de1.s.dots?13+de1.s.xmx:8,0) +output+='h'+len.toFixed(1)+'" stroke-width="1"/>\n';g_close()} +var deco_l_tb={glisq:out_glisq,gliss:out_gliss} +function out_deco_long(x,y,de){var s,p_v,m,nt,i,name=de.dd.glyph,de1=de.start +if(!deco_l_tb[name]){error(1,null,"No function for decoration '$1'",name) +return} +p_v=de.s.p_v +if(de.defl.noen){s=p_v.s_next +while(s&&!s.dur) +s=s.next +if(s){for(m=0;m<=s.nhd;m++){nt=s.notes[m] +if(!nt.a_dd) +continue +for(i=0;i'+ +p+'') +j=p.length>1?2:1 +w+=j*gene.curfont.swfac +dy=''} +str.push('=') +w+=cwidf('=') +if(s.tempo_ca){str.push(s.tempo_ca) +w+=strwh(s.tempo_ca)[0] +j=s.tempo_ca.length+1} +if(s.tempo){str.push(s.tempo) +w+=strwh(s.tempo.toString())[0]}else{p=tempo_note(s,s.new_beat) +str.push(''+ +p+'') +j=p.length>1?2:1 +w+=j*gene.curfont.swfac +dy='y'}} +if(s.tempo_str2){if(dy) +str.push(''+ +s.tempo_str2+'') +else +str.push(s.tempo_str2) +w+=strwh(s.tempo_str2)[0]} +s.tempo_str=str.join(' ') +w+=cwidf(' ')*(str.length-1) +s.tempo_wh=[w,13.0]} +function writempo(s,x,y){var bh +set_font("tempo") +if(gene.curfont.box){gene.curfont.box=false +bh=gene.curfont.size+4} +output+=''+s.tempo_str+'\n' +if(bh){gene.curfont.box=true +output+='\n'} +s.invis=true} +function vskip(h){posy+=h} +function svg_flush(){if(multicol||!output||!user.img_out||posy==0) +return +var i,font,fmt=tsnext?tsnext.fmt:cfmt,w=Math.ceil((fmt.trimsvg||fmt.singleline==1)?(cfmt.leftmargin+img.wx*cfmt.scale+cfmt.rightmargin+2):img.width),head='\n' +if(defs) +head+=''+defs+'\n\n' +if(cfmt.scale!=1){head+='\n';g='\n'} +if(psvg) +psvg.ps_flush(true);if(blkdiv>0){user.img_out(blkdiv==1?'
':'
') +blkdiv=-1} +user.img_out(head+output+g+"");output="" +font_style='' +if(cfmt.fullsvg){defined_glyph={} +for(i=0;i') +blkdiv=0}} +Abc.prototype.blk_flush=blk_flush +var par_sy,cur_sy,voice_tb,curvoice,staves_found,vover,tsfirst +function voice_filter(){var opt +function vfilt(opts,opt){var i,sel=new RegExp(opt) +if(sel.test(curvoice.id)||sel.test(curvoice.nm)){for(i=0;itime) +continue +w=w_tb[s.type] +if(s.type==C.GRACE&&s.next&&s.next.type==C.GRACE) +w-- +if(s.time127) +break +if(wmin==6) +b_chk() +ir=0 +while(1){v=vn[ir++] +if(v==undefined) +break +s=vtb[v] +if(!s||s.time!=time) +continue +w=w_tb[s.type] +if(!w&&s.type==C.GRACE&&s.next&&s.next.type==C.GRACE) +w-- +if(w!=wmin) +continue +if(!w&&s.type==C.PART){if(s.prev) +s.prev.next=s.next +else +s.p_v.sym=s.next +vtb[v]=s.next +if(s.next){s.next.part=s +s.next.prev=s.prev +if(s.soln) +s.next.soln=1} +continue} +if(s.type==C.STAVES) +new_sy=s.sy +if(fl){fl=0;s.seqst=true} +s.ts_prev=prev +prev.ts_next=s +prev=s +vtb[v]=s.next} +if(wmin) +fl=1}} +function voice_adj(sys_chg){var p_voice,s,s2,v,sl +function set_feathered_beam(s1){var s,s2,t,d,b,i,a,d=s1.dur,n=1 +for(s=s1;s;s=s.next){if(s.beam_end||!s.next) +break +n++} +if(n<=1){delete s1.feathered_beam +return} +s2=s;b=d/2;a=d/(n-1);t=s1.time +if(s1.feathered_beam>0){for(s=s1,i=n-1;s!=s2;s=s.next,i--){d=((a*i)|0)+b;s.dur=d;s.time=t;t+=d}}else{for(s=s1,i=0;s!=s2;s=s.next,i++){d=((a*i)|0)+b;s.dur=d;s.time=t;t+=d}} +s.dur=s.time+s.dur-t;s.time=t} +if(curvoice&&curvoice.clone){parse.istart=parse.eol +do_cloning()} +if(par_sy.one_v) +fill_mr_ba(voice_tb[par_sy.top_voice]) +for(v=0;v=staves_found) +break} +for(;s;s=s.next){if(w_tb[s.type]<5&&s.type!=C.STAVES&&s.type!=C.CLEF&&s.time&&(!s.prev||s.time>s.prev.time+s.prev.dur)){s2={type:C.BAR,bar_type:"[]",v:s.v,p_v:s.p_v,st:s.st,time:s.time,dur:0,next:s,prev:s.prev,fmt:s.fmt,invis:1} +if(s.prev) +s.prev.next=s2 +else +voice_tb[s.v].sym=s2 +s.prev=s2} +switch(s.type){case C.GRACE:if(!cfmt.graceword) +continue +for(s2=s.next;s2;s2=s2.next){switch(s2.type){case C.SPACE:continue +case C.NOTE:if(!s2.a_ly) +break +s.a_ly=s2.a_ly;s2.a_ly=null +break} +break} +continue} +if(s.feathered_beam) +set_feathered_beam(s)}}} +function new_syst(init){var st,v,sy_staff,p_voice,sy_new={voices:[],staves:[],top_voice:0} +if(init){cur_sy=par_sy=sy_new +return} +for(v=0;vptim+wmeasure&&s.prev.type!=C.MREST) +return 1 +for(s3=s.next;s3&&s3.time==s.time;s3=s3.next);for(;s3&&!s3.bar_type;s3=s3.next);return s3&&(s3.time-bar_tim)%wmeasure} +for(s=tsfirst;;s=s.ts_next){if(!s) +return +switch(s.type){case C.METER:wmeasure=s.wmeasure +case C.CLEF:case C.KEY:case C.STBRK:continue +case C.BAR:if(s.bar_num) +bar_num=s.bar_num +break} +break} +for(s2=s.ts_next;s2;s2=s2.ts_next){if(s2.type==C.BAR&&s2.time){if(s2.timetim) +break +if(!s2.bar_type) +continue +if(s2.bar_type!='[') +nu=0 +if(s2.text) +txt=s2.text} +if(s.bar_num){bar_num=s.bar_num +ptim=bar_tim=tim +break} +if(wmeasure==1){if(s.bar_dotted) +break +if(txt){if(!cfmt.contbarnb){if(txt[0]=='1') +rep_tim=bar_num +else +bar_num=rep_tim}} +if(!nu) +s.bar_num=++bar_num +break} +n=bar_num+(tim-bar_tim)/wmeasure +k=n-(n|0) +if(cfmt.checkbars&&k&&check_meas()) +error(0,s,"Bad measure duration") +if(tim>ptim+wmeasure){n|=0 +k=0 +bar_tim=tim +bar_num=n} +if(txt){if(txt[0]=='1'){if(!cfmt.contbarnb) +rep_tim=tim-bar_tim +if(!nu) +s.bar_num=n}else{if(!cfmt.contbarnb) +bar_tim=tim-rep_tim +n=bar_num+(tim-bar_tim)/wmeasure +if(n==(n|0)) +s.bar_num=n}}else{n|=0 +s.bar_num=n} +if(!k) +ptim=tim +break}}} +function not2abc(pit,acc){var i,nn='' +if(acc&&acc!=3){if(typeof acc!="object"){nn=['__','_','','^','^^'][acc+2]}else{i=acc[0] +if(i>0){nn+='^'}else{nn+='_' +i=-i} +nn+=i+'/'+acc[1]}} +nn+=ntb[(pit+75)%7] +for(i=pit;i>=23;i-=7) +nn+="'" +for(i=pit;i<16;i+=7) +nn+="," +return nn} +function get_map(text){if(!text) +return +var i,note,notes,map,tmp,ns,ty='',a=text.split(/\s+/) +if(a.length<3){syntax(1,not_enough_p) +return} +ns=a[1] +if(ns[0]=='*'||ns.indexOf("all")==0){ns='all'}else{if(ns.indexOf("octave,")==0||ns.indexOf("key,")==0){ty=ns[0] +ns=ns.split(',')[1] +ns=ns.replace(/[,']+/,'').toUpperCase() +if(ns.indexOf("key,")==0) +ns=ns.replace(/[=^_]+/,'')} +tmp=new scanBuf +tmp.buffer=ns +note=parse_acc_pit(tmp) +if(!note){syntax(1,"Bad note in %%map") +return} +ns=ty+not2abc(note.pit,note.acc)} +notes=maps[a[0]] +if(!notes) +maps[a[0]]=notes={} +map=notes[ns] +if(!map) +notes[ns]=map=[] +a.shift() +a.shift() +if(!a.length) +return +a=info_split(a.join(' ')) +i=0 +if(a[0].indexOf('=')<0){if(a[0][0]!='*'){tmp=new scanBuf;tmp.buffer=a[0];map[1]=parse_acc_pit(tmp)} +if(!a[1]) +return +i++ +if(a[1].indexOf('=')<0){map[0]=a[1].split(',') +i++}} +for(;i=0){var val=parseInt(param) +if(isNaN(val)||val<-36||val>36){syntax(1,errs.bad_transp) +return} +val+=36 +val=((val/12|0)-3)*40+abc2svg.isb40[val%12] +if(param.slice(-1)=='b') +val+=4 +return val}} +Abc.prototype.do_pscom=function(text){var h1,val,s,cmd,param,n,k,b +cmd=text.match(/[^\s]+/) +if(!cmd) +return +cmd=cmd[0];if(curvoice&&curvoice.ignore){switch(cmd){case"staves":case"score":break +default:return}} +param=text.replace(cmd,'').trim() +if(param.slice(-5)==' lock'){fmt_lock[cmd]=true;param=param.slice(0,-5).trim()}else if(fmt_lock[cmd]){return} +switch(cmd){case"clef":if(parse.state>=2){s=new_clef(param) +if(s) +get_clef(s)} +return +case"deco":deco_add(param) +return +case"linebreak":set_linebreak(param) +return +case"map":get_map(param) +return +case"maxsysstaffsep":case"sysstaffsep":if(parse.state==3){val=get_unit(param) +if(isNaN(val)){syntax(1,errs.bad_val,"%%"+cmd) +return} +par_sy.voices[curvoice.v][cmd[0]=='m'?"maxsep":"sep"]=val +return} +break +case"multicol":switch(param){case"start":case"new":case"end":break +default:syntax(1,"Unknown keyword '$1' in %%multicol",param) +return} +s={type:C.BLOCK,subtype:"mc_"+param,dur:0} +if(parse.state>=2){curvoice=voice_tb[0] +curvoice.eoln=1 +sym_link(s) +return} +set_ref(s) +self.block_gen(s) +return +case"ottava":if(parse.state!=3) +return +n=parseInt(param) +if(isNaN(n)||n<-2||n>2||(!n&&!curvoice.ottava)){syntax(1,errs.bad_val,"%%ottava") +return} +k=n +if(n){curvoice.ottava=n}else{n=curvoice.ottava +curvoice.ottava=0} +a_dcn.push(["15mb","8vb","","8va","15ma"][n+2] ++(k?'(':')')) +return +case"repbra":if(curvoice) +curvoice.norepbra=!get_bool(param) +return +case"repeat":if(parse.state!=3) +return +if(!curvoice.last_sym){syntax(1,"%%repeat cannot start a tune") +return} +if(!param.length){n=1;k=1}else{b=param.split(/\s+/);n=parseInt(b[0]);k=parseInt(b[1]) +if(isNaN(n)||n<1||(curvoice.last_sym.type==C.BAR&&n>2)){syntax(1,"Incorrect 1st value in %%repeat") +return} +if(isNaN(k)){k=1}else{if(k<1){syntax(1,"Incorrect 2nd value in %%repeat") +return}}} +parse.repeat_n=curvoice.last_sym.type==C.BAR?n:-n;parse.repeat_k=k +return +case"sep":var h2,len,values,lwidth;set_page();lwidth=img.width-img.lm-img.rm;h1=h2=len=0 +if(param){values=param.split(/\s+/);h1=get_unit(values[0]) +if(values[1]){h2=get_unit(values[1]) +if(values[2]) +len=get_unit(values[2])} +if(isNaN(h1)||isNaN(h2)||isNaN(len)){syntax(1,errs.bad_val,"%%sep") +return}} +if(h1<1) +h1=14 +if(h2<1) +h2=h1 +if(len<1) +len=90 +if(parse.state>=2){s=new_block(cmd);s.x=(lwidth-len)/2/cfmt.scale;s.l=len/cfmt.scale;s.sk1=h1;s.sk2=h2 +return} +vskip(h1);output+='\n';vskip(h2);blk_flush() +return +case"setbarnb":val=parseInt(param) +if(isNaN(val)||val<1){syntax(1,"Bad %%setbarnb value") +break} +glovar.new_nbar=val +return +case"staff":if(parse.state!=3) +return +val=parseInt(param) +if(isNaN(val)){syntax(1,"Bad %%staff value '$1'",param) +return} +var st +if(param[0]=='+'||param[0]=='-') +st=curvoice.cst+val +else +st=val-1 +if(st<0||st>nstaff){syntax(1,"Bad %%staff number $1 (cur $2, max $3)",st,curvoice.cst,nstaff) +return} +delete curvoice.floating;curvoice.cst=st +return +case"staffbreak":if(parse.state!=3) +return +s={type:C.STBRK,dur:0} +if(param.slice(-1)=='f'){s.stbrk_forced=true +param=param.replace(/\sf$/,'')} +if(param){val=get_unit(param) +if(isNaN(val)){syntax(1,errs.bad_val,"%%staffbreak") +return} +s.xmx=val}else{s.xmx=14} +sym_link(s) +return +case"tacet":if(param[0]=='"') +param=param.slice(1,-1) +case"stafflines":case"staffscale":case"staffnonote":set_v_param(cmd,param) +return +case"staves":case"score":if(!parse.state) +return +if(parse.scores&&parse.scores.length>0){text=parse.scores.shift();cmd=text.match(/([^\s]+)\s*(.*)/);param=cmd[2] +cmd=cmd[1]} +get_staves(cmd,param) +return +case"center":case"text":k=cmd[0]=='c'?'c':cfmt.textoption +set_font("text") +if(parse.state>=2){s=new_block("text") +s.text=param +s.opt=k +s.font=cfmt.textfont +return} +write_text(param,k) +return +case"transpose":if(cfmt.sound) +return +val=get_transp(param) +if(val==undefined){val=get_interval(param) +if(val==undefined) +return} +switch(parse.state){case 0:cfmt.transp=0 +case 1:cfmt.transp=(cfmt.transp||0)+val +return} +curvoice.shift=val +key_trans() +return +case"tune":return +case"user":set_user(param) +return +case"voicecolor":if(curvoice) +curvoice.color=param +return +case"vskip":val=get_unit(param) +if(isNaN(val)){syntax(1,errs.bad_val,"%%vskip") +return} +if(val<0){syntax(1,"%%vskip cannot be negative") +return} +if(parse.state>=2){s=new_block(cmd);s.sk=val +return} +vskip(val);return +case"newpage":case"leftmargin":case"rightmargin":case"pagescale":case"pagewidth":case"printmargin":case"scale":case"staffwidth":if(parse.state>=2){s=new_block(cmd);s.param=param +return} +if(cmd=="newpage"){blk_flush() +if(user.page_format) +blkdiv=2 +return} +break} +self.set_format(cmd,param)} +Abc.prototype.do_begin_end=function(type,opt,text){var i,j,action,s +switch(type){case"js":js_inject(text) +break +case"ml":if(cfmt.pageheight){syntax(1,"Cannot have %%beginml with %%pageheight") +break} +if(parse.state>=2){s=new_block(type);s.text=text}else{blk_flush() +if(user.img_out) +user.img_out(text)} +break +case"svg":j=0 +while(1){i=text.indexOf('',i) +j=text.indexOf('',i) +if(j<0){syntax(1,"No in %%beginsvg sequence") +break} +style+=text.slice(i+1,j).replace(/\s+$/,'')} +j=0 +while(1){i=text.indexOf('\n',j) +if(i<0) +break +j=text.indexOf('',i) +if(j<0){syntax(1,"No in %%beginsvg sequence") +break} +defs_add(text.slice(i+6,j))} +break +case"text":action=get_textopt(opt);if(!action) +action=cfmt.textoption +set_font("text") +if(text.indexOf('\\')>=0) +text=cnv_escape(text) +if(parse.state>1){s=new_block(type);s.text=text +s.opt=action +s.font=cfmt.textfont +break} +write_text(text,action) +break}} +function generate(){var s,v,p_voice;if(a_dcn.length){syntax(1,"Decoration without symbol") +a_dcn=[]} +if(parse.tp){syntax(1,"No end of tuplet") +s=parse.tps +if(s) +delete s.tp +delete parse.tp} +if(vover){syntax(1,"No end of voice overlay");get_vover(vover.bar?'|':')')} +voice_adj();sort_all() +if(tsfirst){for(v=0;v7){n-=12 +curvoice.tr_sco+=4} +s.k_sf=n +for(b40=0;b40<40;b40++){if(abc2svg.b40l5[b40]==n) +break} +s.k_b40=b40 +if(!s.k_a_acc) +return +d=b40-s.orig.k_b40 +a_acc=[] +for(i=0;imxt){p_v2=voice_tb[v] +mxt=p_v2.time}} +if(p_v.time>=mxt) +return +var p_v_sav=curvoice,dur=mxt-p_v.time,s={type:C.MREST,stem:0,multi:0,nhd:0,xmx:0,frm:1,dur:dur,dur_orig:dur,nmes:dur/p_v.wmeasure,notes:[{pit:18,dur:dur}],tacet:p_v.tacet},s2={type:C.BAR,bar_type:'|',dur:0,multi:0} +if(p_v2.last_sym.bar_type) +s2.bar_type=p_v2.last_sym.bar_type +glovar.mrest_p=1 +curvoice=p_v +sym_link(s) +sym_link(s2) +curvoice=p_v_sav} +function get_staves(cmd,parm){var s,p_voice,p_voice2,i,flags,v,vid,a_vf,st,range,nv=voice_tb.length,maxtime=0 +if(curvoice&&curvoice.clone){i=parse.eol +parse.eol=parse.bol +do_cloning() +parse.eol=i} +if(parm){a_vf=parse_staves(parm) +if(!a_vf) +return}else if(staves_found<0){syntax(1,errs.bad_val,'%%'+cmd) +return} +for(v=0;vmaxtime) +maxtime=p_voice.time} +if(!maxtime){par_sy.staves=[] +par_sy.voices=[]}else{voice_adj(true) +for(v=0;v0&&p_voice.norepbra==undefined&&!(par_sy.staves[st-1].flags&STOP_BAR)) +p_voice.norepbra=true} +curvoice=parse.state>=2?voice_tb[par_sy.top_voice]:null} +function clone_voice(id){var v,p_voice +for(v=0;vvover.p_voice.time) +vover.p_voice.time=curvoice.time} +curvoice.acc=[] +p_voice2=vover.p_voice +s=curvoice.last_sym +if(s.type==C.SPACE&&p_voice2.last_sym.type!=C.SPACE){s.p_v=p_voice2 +s.v=s.p_v.v +while(s.prev.type==C.SPACE){s=s.prev +s.p_v=p_voice2 +s.v=s.p_v.v} +s2=s.prev +s2.next=null +s.prev=p_voice2.last_sym +s.prev.next=s +p_voice2.last_sym=curvoice.last_sym +curvoice.last_sym=s2} +curvoice=p_voice2 +vover=null +return} +if(type=='('){if(vover){syntax(1,"Voice overlay already started") +return} +vover={p_voice:curvoice,time:curvoice.time} +return} +if(!curvoice.last_note){syntax(1,errs.nonote_vo) +return} +curvoice.last_note.beam_end=true;p_voice2=curvoice.voice_down +if(!p_voice2){p_voice2=clone_voice(curvoice.id+'o');curvoice.voice_down=p_voice2;p_voice2.time=0;p_voice2.second=true;p_voice2.last_note=null +v2=p_voice2.v;if(par_sy.voices[curvoice.v]){par_sy.voices[v2]={st:curvoice.st,second:true} +range=par_sy.voices[curvoice.v].range +for(v=0;vrange) +par_sy.voices[v].range++} +par_sy.voices[v2].range=range+1}} +p_voice2.ulen=curvoice.ulen +p_voice2.dur_fact=curvoice.dur_fact +p_voice2.acc=[] +if(!vover){time=p_voice2.time +if(curvoice.ignore) +s=null +else +for(s=curvoice.last_sym;;s=s.prev){if(s.type==C.BAR||s.time<=time) +break} +vover={bar:(s&&s.bar_type)?s.bar_type:'|',p_voice:curvoice,time:s?s.time:curvoice.time}}else{if(curvoice!=vover.p_voice&&curvoice.time!=vover.p_voice.time){syntax(1,"Wrong duration in voice overlay") +if(curvoice.time>vover.p_voice.time) +vover.p_voice.time=curvoice.time}} +p_voice2.time=vover.time;curvoice=p_voice2} +function is_voice_sig(){var s +if(curvoice.time) +return false +if(!curvoice.last_sym) +return true +for(s=curvoice.last_sym;s;s=s.prev) +if(w_tb[s.type]) +return false +return true} +function get_clef(s){var s2,s3 +if(s.clef_type=='p'){s2=curvoice.ckey +s2.k_drum=1 +s2.k_sf=0 +s2.k_b40=2 +s2.k_map=abc2svg.keys[7] +if(!curvoice.key) +curvoice.key=s2} +if(!curvoice.time&&is_voice_sig()){curvoice.clef=s +s.fmt=cfmt +return} +if(s.clef_none) +s2=null +else +for(s2=curvoice.last_sym;s2&&s2.time==curvoice.time;s2=s2.prev){if(w_tb[s2.type]) +break} +if(s2&&s2.time==curvoice.time&&s2.k_sf!=undefined){s3=s2 +s2=s2.prev} +if(s2&&s2.time==curvoice.time&&s2.bar_type&&s2.bar_type[0]!=':') +s3=s2 +if(s3){s2=curvoice.last_sym +curvoice.last_sym=s3.prev +sym_link(s) +s.next=s3 +s3.prev=s +curvoice.last_sym=s2 +if(s.soln){delete s.soln +curvoice.eoln=true}}else{sym_link(s)} +if(s.prev) +s.clef_small=1} +function get_key(parm){var v,p_voice,a=new_key(parm),s_key=a[0],s=s_key,empty=s.k_sf==undefined&&!s.k_a_acc +a=a[1] +if(empty) +s.invis=1 +else +s.orig=s +if(parse.state==1){parse.ckey=s +if(empty){s_key.k_sf=0;s_key.k_none=true +s_key.k_map=abc2svg.keys[7]} +for(v=0;v0) +vs=vid.split(',') +else +vs=[vid] +if(parse.state<2){while(1){vid=vs.shift() +if(!vid) +break +if(a.length) +memo_kv_parm(vid,a) +if(vid!='*'&&parse.state==1) +curvoice=new_voice(vid)} +return} +if(vid=='*'){syntax(1,"Cannot have V:* in tune body") +return} +curvoice=new_voice(vs[0]) +if(vs.length>1){vs.shift() +curvoice.clone={vs:vs,a:a.slice(0),bol:parse.iend} +if(parse.file[curvoice.clone.bol-1]!=']') +curvoice.clone.bol++} +set_kv_parm(a) +key_trans() +v=curvoice.v +if(curvoice.new){delete curvoice.new +if(staves_found<0){curvoice.st=curvoice.cst=++nstaff;par_sy.nstaff=nstaff;par_sy.voices[v]={st:nstaff,range:v} +par_sy.staves[nstaff]={stafflines:curvoice.stafflines||"|||||",staffscale:1}}else if(!par_sy.voices[v]){curvoice.ignore=1 +return}} +if(!curvoice.filtered&&par_sy.voices[v]&&(parse.voice_opts||parse.tune_v_opts)){curvoice.filtered=true;voice_filter()}} +function goto_tune(){var v,p_voice +set_page();write_heading();if(glovar.new_nbar){gene.nbar=glovar.new_nbar +glovar.new_nbar=0}else{gene.nbar=1} +parse.state=3 +for(v=0;v=0){p_voice=voice_tb[v];delete p_voice.new;p_voice.st=p_voice.cst=v;par_sy.voices[v]={st:v,range:v} +par_sy.staves[v]={stafflines:p_voice.stafflines||"|||||",staffscale:1}}}} +function get_sym(p,cont){var s,c,i,j,d +if(curvoice.ignore) +return +if(cont){s=curvoice.sym_cont +if(!s){syntax(1,"+: symbol line without music") +return}}else{if(curvoice.sym_restart){curvoice.sym_start=curvoice.sym_restart;curvoice.sym_restart=null} +s=curvoice.sym_start +if(!s) +s=curvoice.sym +if(!s){syntax(1,"s: without music") +return}} +i=0 +while(1){while(p[i]==' '||p[i]=='\t') +i++;c=p[i] +if(!c) +break +switch(c){case'|':while(s&&s.type!=C.BAR) +s=s.next +if(!s){syntax(1,"Not enough measure bars for symbol line") +return} +s=s.next;i++ +continue +case'!':case'"':j=++i +i=p.indexOf(c,j) +if(i<0){syntax(1,c=='!'?"No end of decoration":"No end of chord symbol/annotation");i=p.length +continue} +d=p.slice(j-1,i+1) +break +case'*':break +default:d=c.charCodeAt(0) +if(d<128){d=char_tb[d] +if(d.length>1&&(d[0]=='!'||d[0]=='"')){c=d[0] +break}} +syntax(1,errs.bad_char,c) +break} +while(s&&s.type!=C.NOTE) +s=s.next +if(!s){syntax(1,"Too many elements in symbol line") +return} +switch(c){default:break +case'!':a_dcn.push(d.slice(1,-1)) +deco_cnv(s,s.prev) +break +case'"':parse_gchord(d) +if(a_gch) +csan_add(s) +break} +s=s.next;i++} +curvoice.sym_cont=s} +function get_lyrics(text,cont){var s,word,p,i,j,ly,dfnt,ln,c,cf +if(curvoice.ignore) +return +if((curvoice.pos.voc&0x07)!=C.SL_HIDDEN) +curvoice.have_ly=true +if(cont){s=curvoice.lyric_cont +if(!s){syntax(1,"+: lyric without music") +return} +dfnt=get_font("vocal") +if(gene.deffont!=dfnt){if(gene.curfont==gene.deffont) +gene.curfont=dfnt +gene.deffont=dfnt}}else{set_font("vocal") +if(curvoice.lyric_restart){curvoice.lyric_start=s=curvoice.lyric_restart;curvoice.lyric_restart=null;curvoice.lyric_line=0}else{curvoice.lyric_line++;s=curvoice.lyric_start} +if(!s) +s=curvoice.sym +if(!s){syntax(1,"w: without music") +return}} +p=text;i=0 +cf=gene.curfont +while(1){while(p[i]==' '||p[i]=='\t') +i++ +if(!p[i]) +break +ln=0 +j=parse.istart+i+2 +switch(p[i]){case'|':while(s&&s.type!=C.BAR) +s=s.next +if(!s){syntax(1,"Not enough measure bars for lyric line") +return} +s=s.next;i++ +continue +case'-':case'_':word=p[i] +ln=p[i]=='-'?2:3 +break +case'*':word="" +break +default:word="";while(1){if(!p[i]) +break +switch(p[i]){case'_':case'*':case'|':i-- +case' ':case'\t':break +case'~':word+=' ' +i++ +continue +case'-':ln=1 +break +case'\\':if(!p[++i]) +continue +word+=p[i++] +continue +case'$':word+=p[i++] +c=p[i] +if(c=='0') +gene.curfont=gene.deffont +else if(c>='1'&&c<='9') +gene.curfont=get_font("u"+c) +default:word+=p[i++] +continue} +break} +break} +while(s&&s.type!=C.NOTE) +s=s.next +if(!s){syntax(1,"Too many words in lyric line") +return} +if(word&&(s.pos.voc&0x07)!=C.SL_HIDDEN){ly={t:word,font:cf,istart:j,iend:j+word.length} +if(ln) +ly.ln=ln +if(!s.a_ly) +s.a_ly=[] +s.a_ly[curvoice.lyric_line]=ly +cf=gene.curfont} +s=s.next;i++} +curvoice.lyric_cont=s} +function ly_set(s){var i,j,ly,d,s1,s2,p,w,spw,xx,sz,shift,dw,s3=s,wx=0,wl=0,n=0,dx=0,a_ly=s.a_ly,align=0 +for(s2=s.ts_next;s2;s2=s2.ts_next){if(s2.shrink){dx+=s2.shrink +n++} +if(s2.bar_type){dx+=3 +break} +if(!s2.a_ly) +continue +i=s2.a_ly.length +while(--i>=0){ly=s2.a_ly[i] +if(!ly) +continue +if(!ly.ln||ly.ln<2) +break} +if(i>=0) +break} +for(i=0;i]*>/g,'') +if(ly.ln>=2){ly.shift=0 +continue} +spw=cwid(' ')*ly.font.swfac +w=ly.t.wh[0] +if(s.type==C.GRACE){shift=s.wl}else if((p[0]>='0'&&p[0]<='9'&&p.length>2)||p[1]==':'||p[0]=='('||p[0]==')'){if(p[0]=='('){sz=spw}else{j=p.indexOf(' ') +set_font(ly.font) +if(j>0) +sz=strwh(p.slice(0,j))[0] +else +sz=w*.2} +shift=(w-sz)*.4 +if(shift>14) +shift=14 +shift+=sz +if(p[0]>='0'&&p[0]<='9'){if(shift>align) +align=shift}}else{shift=w*.4 +if(shift>14) +shift=14} +ly.shift=shift +if(shift>wl) +wl=shift +w+=spw*1.5 +w-=shift +if(w>wx) +wx=w} +while(!s3.seqst) +s3=s3.ts_prev +if(s3.ts_prev&&s3.ts_prev.bar_type) +wl-=4 +if(s3.wl0){for(i=0;i='0'&&ly.t[0]<='9') +ly.shift=align}}} +function draw_lyric_line(p_voice,j,y){var p,lastx,w,s,s2,ly,lyl,ln,hyflag,lflag,x0,shift +if(p_voice.hy_st&(1<=2){if(x0==0&&lastx>s.x-18) +lastx=s.x-18 +if(ln==2) +hyflag=true +else +lflag=true;x0=s.x-shift +continue} +x0=s.x-shift;if(ln) +hyflag=true +if(user.anno_start||user.anno_stop){s2={p_v:s.p_v,st:s.st,istart:ly.istart,iend:ly.iend,ts_prev:s,ts_next:s.ts_next,x:x0,y:y,ymn:y,ymx:y+gene.curfont.size,wl:0,wr:w} +anno_start(s2,'lyrics')} +xy_str(x0,y,p) +anno_stop(s2,'lyrics') +lastx=x0+w} +if(hyflag){hyflag=false;x0=realwidth-10 +if(x00){if(y>-tsfirst.fmt.vocalspace) +y=-tsfirst.fmt.vocalspace;y*=sc +for(j=0;j=0;){draw_lyric_line(p_voice,j,y+a_h[j]*.22) +y+=a_h[j]*1.1} +return y/sc} +function draw_all_lyrics(){var p_voice,s,v,nly,i,x,y,w,a_ly,ly,lyst_tb=new Array(nstaff+1),nv=voice_tb.length,h_tb=new Array(nv),nly_tb=new Array(nv),above_tb=new Array(nv),rv_tb=new Array(nv),top=0,bot=0,st=-1 +for(v=0;vy) +bot=y +while(nlyh_tb[v][i]) +h_tb[v][i]=ly.t.wh[1]}}}else{y=y_get(p_voice.st,1,0,realwidth) +if(topy) +bot=y} +if(!lyst_tb[st]) +lyst_tb[st]={} +lyst_tb[st].top=top;lyst_tb[st].bot=bot;nly_tb[v]=nly +if(nly==0) +continue +if(p_voice.pos.voc) +above_tb[v]=(p_voice.pos.voc&0x07)==C.SL_ABOVE +else if(voice_tb[v+1]&&voice_tb[v+1].st==st&&voice_tb[v+1].have_ly) +above_tb[v]=true +else +above_tb[v]=false +if(above_tb[v]) +lyst_tb[st].a=true +else +lyst_tb[st].b=true} +i=0 +for(v=0;v0) +lyst_tb[st].bot=draw_lyrics(p_voice,nly_tb[v],h_tb[v],lyst_tb[st].bot,1)} +while(--i>=0){v=rv_tb[i];p_voice=voice_tb[v];st=p_voice.st;set_dscale(st,true);lyst_tb[st].top=draw_lyrics(p_voice,nly_tb[v],h_tb[v],lyst_tb[st].top,-1)} +for(v=0;v0){for(s=p_voice.sym;s;s=s.next){if(s.a_ly){y_set(st,0,s.x-2,10,bot)}}}else{y_set(st,0,0,realwidth,bot)}}}} +function parse_gchord(type){var c,text,gch,x_abs,y_abs,i,j,istart,iend,ann_font=get_font("annotation"),h_ann=ann_font.size,line=parse.line +function get_float(){var txt='' +while(1){c=text[i++] +if("1234567890.-".indexOf(c)<0) +return parseFloat(txt) +txt+=c}} +istart=parse.bol+line.index +if(type.length>1){text=type.slice(1,-1);iend=istart+1}else{i=++line.index +while(1){j=line.buffer.indexOf('"',i) +if(j<0){syntax(1,"No end of chord symbol/annotation") +return} +if(line.buffer[j-1]!='\\'||line.buffer[j-2]=='\\') +break +i=j+1} +text=cnv_escape(line.buffer.slice(line.index,j)) +line.index=j +iend=parse.bol+line.index+1} +if(ann_font.pad) +h_ann+=ann_font.pad +i=0;type='g' +while(1){c=text[i] +if(!c) +break +gch={text:"",istart:istart,iend:iend,font:ann_font} +switch(c){case'@':type=c;i++;x_abs=get_float() +if(c!=','){syntax(1,"',' lacking in annotation '@x,y'");y_abs=0}else{y_abs=get_float() +if(c!=' ') +i--} +gch.x=x_abs;gch.y=y_abs +break +case'^':gch.pos=C.SL_ABOVE +case'_':if(c=='_') +gch.pos=C.SL_BELOW +case'<':case'>':i++;type=c +break +default:switch(type){case'g':gch.font=get_font("gchord") +gch.pos=curvoice.pos.gch||C.SL_ABOVE +break +case'^':gch.pos=C.SL_ABOVE +break +case'_':gch.pos=C.SL_BELOW +break +case'@':gch.x=x_abs;y_abs-=h_ann;gch.y=y_abs +break} +break} +gch.type=type +while(1){c=text[i] +if(!c) +break +switch(c){case'\\':c=text[++i] +if(c=='n') +break +gch.text+='\\' +if(!c) +break +default:gch.text+=c;i++ +continue +case'&':while(1){gch.text+=c;c=text[++i] +switch(c){default:continue +case';':case undefined:case'\\':break} +break} +if(c==';'){i++;gch.text+=c +continue} +break +case';':break} +i++ +break} +gch.otext=gch.text +if(!a_gch) +a_gch=[] +a_gch.push(gch)}} +function gch_tr1(p,tr){var i,o,n,ip,csa=p.split('/') +tr=abc2svg.b40l5[(tr+202)%40] +for(i=0;i=13?'##':n>=6?'#':n<=-9?'bb':n<=-2?'b':'') ++p.slice(ip)} +return csa.join('/')} +function csan_add(s){var i,gch +if(s.type==C.BAR){for(i=0;i8) +xspc=8;gch.x=-xspc;break +case'<':gch.x=-(wh[0]+6);y_left-=wh[1];gch.y=y_left+wh[1]/2 +break +case'>':gch.x=6;y_right-=wh[1];gch.y=y_right+wh[1]/2 +break}} +y_left/=2;y_right/=2 +for(ix=0;ix':gch.y-=y_right +break}}} +Abc.prototype.draw_gchord=function(i,s,x,y){if(s.invis&&s.play) +return +var y2,an=s.a_gch[i],h=an.text.wh[1],pad=an.font.pad,w=an.text.wh[0]+pad*2,dy=h*.22 +if(an.font.figb){h*=2.4 +dy+=an.font.size*1.3} +switch(an.type){case'_':y-=h+pad +break +case'^':y+=pad +break +case'<':case'>':if(an.type=='<'){if(s.notes[0].acc) +x-=s.notes[0].shac +x-=pad}else{if(s.xmx) +x+=s.xmx +if(s.dots) +x+=1.5+3.5*s.dots +x+=pad} +y+=(s.type==C.NOTE?(((s.notes[s.nhd].pit+s.notes[0].pit)>>1)- +18)*3:12) +-h/2 +break +default:if(y>=0) +y+=pad +else +y-=h+pad +break +case'@':y+=(s.type==C.NOTE?(((s.notes[s.nhd].pit+s.notes[0].pit)>>1)- +18)*3:12) +-h/2 +if(y>0){y2=y+h+pad+2 +if(y2>staff_tb[s.st].ann_top) +staff_tb[s.st].ann_top=y2}else{y2=y-2 +if(y2=0) +y_set(s.st,1,x,w,y+h+pad+2) +else +y_set(s.st,0,x,w,y-pad)} +use_font(an.font) +set_font(an.font) +set_dscale(s.st) +if(user.anno_start) +user.anno_start("annot",an.istart,an.iend,x-2,y+h+2,w+4,h+4,s) +xy_str(x,y+dy,an.text) +if(user.anno_stop) +user.anno_stop("annot",an.istart,an.iend,x-2,y+h+2,w+4,h+4,s)} +function draw_all_chsy(){var s,san1,an,i,x,y,w,n_an=0,minmax=new Array(nstaff+1) +function set_an_yu(j){var an,i,s,x,y,w +for(s=san1;s;s=s.ts_next){an=s.a_gch +if(!an) +continue +i=an.length-j-1 +an=an[i] +if(!an) +continue +if(an.pos==C.SL_ABOVE){x=s.x+an.x +w=an.text.wh[0] +if(w&&x+w>realwidth) +x=realwidth-w +y=y_get(s.st,1,x,w) +if(an.type=='g'&&yrealwidth) +x=realwidth-w +y=y_get(s.st,0,x,w)-2 +if(an.type=='g'&&y>minmax[s.st].ydn) +y=minmax[s.st].ydn +self.draw_gchord(i,s,x,y)}} +for(i=0;i<=nstaff;i++) +minmax[i]={ydn:staff_tb[i].botbar-3,yup:staff_tb[i].topbar+4} +for(s=tsfirst;s;s=s.ts_next){an=s.a_gch +if(!an) +continue +if(!san1) +san1=s +i=an.length +if(i>n_an) +n_an=i +while(--i>=0){if(an[i].type=='g'){an=an[i] +x=s.x+an.x +w=an.text.wh[0] +if(w&&x+w>realwidth) +x=realwidth-w +if(an.pos==C.SL_ABOVE){y=y_get(s.st,true,x,w) +if(y>minmax[s.st].yup) +minmax[s.st].yup=y}else if(an.pos==C.SL_BELOW){y=y_get(s.st,false,x,w)-2 +if(ye+1){item=xml.slice(e+1,i).replace(/\s*/,'') +if(item){if(!o.children) +o.children=[] +o.children.push({name:"text",content:item})}} +e=xml.indexOf('>',i) +if(e<0) +return root +switch(xml[i+1]){case'?':continue +case'!':e=xml.indexOf('-->',i) +if(e<0) +return root +continue +case'/':item=xml.slice(i+2,e) +if(item!=o.name){error(1,null,'Found tag instead of ') +continue} +o=stack.pop() +if(!o) +error(1,null,'No start tag "'+item+'"') +continue} +item=xml.slice(i+1,e).trim() +no={name:item.match(/[\w-]+/)[0],ix:i} +if(!o.children) +o.children=[] +o.children.push(no) +if(item.slice(-1)!='/'){stack.push(o) +o=no}else{item=item.slice(0,-1).trim()} +item=item.replace(/[^\s]*\s*/,'') +if(!item) +continue +n=item.match(/=|[^\s"=]+|".*?"/g) +for(j=0;j(") +add_dcn(s2,">)") +break}},harm:set_text,incip:function(tag){return true},l:function(tag){if(curr.text) +curr.text+="\n"},label:function(tag){if(curr.st==undefined&&!curr.staffGrp) +return true +set_text(tag)},layer:function(tag){var v,p_v,old_sy,s,vid,t,st=curr.st,staff=par_sy.staves[st],n=tag.n +if(parse.state<2){staves_found=0 +if(staff["meter.sym"]||staff["meter.count"]){new_meter(metercnv(staff)) +curr.mu=meteru(staff) +delete staff["meter.sym"] +delete staff["meter.count"]} +get_key(staff["key.sig"]?keycnv(staff):"C")} +vid=(curr.st+1).toString() +if(n&&n!="1") +vid+=n +p_v=new_voice(vid) +if(p_v.new){v=p_v.v +p_v.st=p_v.cst=st +p_v.time=curr.meastim +if(!par_sy.voices[0]) +par_sy.top_voice=v +if(n) +n=Number(n)-1 +else +n=0 +par_sy.voices[v]={st:st} +if(!par_sy.rv) +par_sy.rv=[] +par_sy.rv.push({v:v,r:st*10+n}) +par_sy.rv.sort(function(n1,n2){return n1.r-n2.r}) +for(n=0;n=0){if(par_sy.voices[v].st==st){p_v.second=par_sy.voices[p_v.v].second=true +break}}} +if(tag.label){t=tag}else if(staff.label){t=staff.label +staff.label=null} +if(t){p_v.nm=t.label +p_v.new_name=true +if(t["label.abbr"]) +p_v.snm=t["label.abbr"]} +get_voice(vid) +if(staff["clef.shape"]){s=new_clef(clefcnv(staff)) +delete staff["clef.shape"] +if(s){get_clef(s) +if(s.prev&&s.prev.bar_type){s.next=s.prev +if(s.prev.prev) +s.prev.prev.next=s +else +s.p_v.sym=s +s.prev=s.prev.prev +s.next.prev=s +s.next.next=null +curvoice.last_sym=s.next}}}},lb:function(tag){curr.text+="\n"},mei:function(tag){if(!tag.meiversion||tag.meiversion[0]!="4") +error(0,null,"Bad MEI version "+tag.meiversion)},meter:function(tag){var m +if(tag.count){m=tag.count +if(tag.unit) +m+="/"+tag.unit +new_meter(m) +m=C.BLEN/(tag.unit?Number(tag.unit):1)}else switch(tag.sym){case"common":new_meter("C");m=4;break +case"cut":new_meter("C|");m=2;break} +if(m) +curr.mu=C.BLEN/m},mRest:function(tag){var s={type:C.REST,istart:tag.ix,stem:0,multi:0,nhd:0,xmx:0,dur:curvoice.wmeasure,dur_orig:curvoice.wmeasure} +sym_link(s) +if(curvoice.wmeasure==1) +curr.mrest=true +if(par_sy.staves[s.st].invis) +s.invis=true},measure:function(tag){curr.meas=tag.n},mordent:function(tag){var ty,s=get_ref(tag) +if(!s) +return +if(s.s) +s=s.s +switch(tag.form){default:ty="uppermordent";break +case"lower":ty="lowermordent";break} +add_dcn(s,ty)},multiRest:function(tag){var dur=curvoice.wmeasure*tag.num,s={type:C.MREST,istart:tag.ix,stem:0,multi:0,nhd:0,xmx:0,dur:dur,dur_orig:dur} +sym_link(s)},music:function(tag){info.X="1" +parse.fname="mei" +parse.state=1},note:function(tag){var note,s,acc,pit,dur,v,i,vi,gr,ref,st,oct=tag.oct,id=tag["xml:id"] +if(!tag.pname){if(tag["pname.ges"]){tag.pname=tag["pname.ges"]}else if(tag.sameas){ref=tag.sameas +if(ref[0]=='#') +ref=ref.slice(1) +s=clone(curr.ids[ref]) +if(!s.type){note=s +s=null}else{s.notes=clone(s.notes) +s.notes[0]=note=clone(s.notes[0]) +note=s}}else{return}} +if(!note){if(!oct) +oct=curr.oct +note={pit:"cdefgab".indexOf(tag.pname.toLowerCase())+ +oct*7-12,shhd:0,shac:0}} +if(curr.chord){s=curr.chord +dur=s.dur +s.notes[++s.nhd]=note}else{dur=durcnv(tag) +if(!s){s={type:C.NOTE,stem:0,multi:0,nhd:0,xmx:0,dur:dur,dur_orig:dur,notes:[note]}} +s.istart=tag.ix +switch(tag["stem.dir"]){case"up":s.stem=1;break +case"down":s.stem=-1;break} +if(tag.grace){gr=curvoice.last_sym +if(gr&&gr.type!=C.GRACE){gr={type:C.GRACE,istart:tag.ix,dur:0,stem:0,multi:0} +sym_link(gr) +gr.extra=s}else{gr=gr.extra +while(gr.next) +gr=gr.next +gr.next=s +s.prev=gr} +s.cst=s.st=curvoice.st +s.v=curvoice.v +s.p_v=curvoice}else{switch(curr.beam){case 0:s.beam_st=true +s.beam_end=true +break +case 1:s.beam_st=true +curr.beam=2 +break} +sym_link(s)}} +if(id){note.s=s +note.m=s.nhd +do_delayed(id,curr.chord?note:s)} +note.dur=dur +if(acc_ty[tag.accid]) +note.acc=acc_ty[tag.accid] +if(tag.staff) +s.st=curr.st_cnv[tag.staff-1] +if(tag.grace) +s.grace=true +switch(tag.tuplet){case"i1":curr.tp.push(s) +break +case"t1":if(curr.tp.length) +tp_set(curr.tp.pop(),{num:3,numbase:2}) +break} +if(tag.color) +s.color=tag.color +curr.s=s +curr.oct=oct +if(tag.artic) +artic(s,tag) +switch(tag.breaksec){case"1":s.beam_br1=true;break +case"2":s.beam_br2=true;break}},pb:function(tag){var s +if(parse.state>=2){s=new_block("newpage") +s.param=tag.n||""}else{blk_flush() +block.newpage=true}},pedal:function(tag){var dcn,s=get_ref(tag) +if(!s) +return +if(s.s) +s=s.s +switch(tag.dir){default:dcn="ped)";break +case"down":dcn="ped(";break} +add_dcn(s,dcn)},rend:function(tag){var font,n,f="" +if(tag.fontfam) +f+=" "+tag.fontfam +if(tag.fontname) +f+=" "+tag.fontname +if(tag.fontweight) +f+=" "+tag.fontweight +if(tag.fontstyle) +f+=" "+tag.fontstyle +if(!f) +f="*" +else +f=f.slice(1) +if(tag.fontsize){n=parseInt(tag.fontsize)*1.33 +f+=" "+n.toString()}else{f+=" *"} +if(f!="* *"){n=curr.fnt[f] +if(!n){n=++curr.ftn +curr.fnt[f]=n +param_set_font("setfont-"+n,f)} +curr.text+='$'+n +curr.rend=true}},rest:function(tag){var id=tag["xml:id"],dur=durcnv(tag),s={type:C.REST,istart:tag.ix,stem:0,multi:0,nhd:0,xmx:0,dur:dur,dur_orig:dur} +if(tag.name=="rest"){switch(curr.beam){case 0:s.beam_st=true +s.beam_end=true +break +case 1:s.beam_st=true +curr.beam=2 +break}}else{s.invis=true} +sym_link(s) +if(id) +do_delayed(id,s) +switch(tag.tuplet){case"i1":curr.tp.push(s) +break +case"t1":if(curr.tp.length) +tp_set(curr.tp.pop(),{num:3,numbase:2}) +break}},revisionDesc:function(tag){return true},sb:function(tag){voice_tb[0].eoln=true},scoreDef:function(tag){var w,h,v,tmp +curr.scoreDef=tag +if(parse.state>=2){if(tag["key.sig"]){tmp=keycnv(tag) +for(v=0;v1.3?1123.66/h:1056/h} +if(tag["page.leftmar"]) +self.set_format("leftmargin",unitcnv(tag["page.leftmar"])) +if(tag["page.rightmar"]) +self.set_format("rightmargin",unitcnv(tag["page.rightmar"])) +if(tag["page.scale"]) +self.set_format("pagescale",tag["page.scale"]) +if(tag["page.width"]) +self.set_format("pagewidth",unitcnv(tag["page.width"]))},slur:function(tag){var sl,s1=get_ref(tag),s2=get_ref(tag,s1),ty=C.SL_AUTO +if(!s1||!s2) +return +switch(tag.curvedir){case"above":ty=C.SL_ABOVE;break +case"below":ty=C.SL_BELOW;break} +switch(tag.lform){case"dashed":case"dotted":ty|=C.SL_DOTTED;break} +sl={ty:ty,ss:s1.s||s1,se:s2.s||s2} +if(s1.s) +sl.nts=s1 +if(s2.s) +sl.nte=s2 +if(!s1.sls) +s1.sls=[] +s1.sls.push(sl)},sourceDesc:function(tag){return true},staff:function(tag){curr.st=curr.st_cnv[tag.n-1] +if(curr.st==undefined){error(1,null,"Unknown staff "+tag.n) +return true} +var staff=par_sy.staves[curr.st] +switch(tag.visible){case"false":staff.invis=true +break +case"true":staff.invis=false +break}},staffDef:function(tag){var k,st,def,staff,sy,n=tag.n-1,grp=curr.staffGrp +if(!curr.st_cnv) +curr.st_cnv=[] +else +st=curr.st_cnv[n] +if(st==undefined){staff={stafflines:'|||||',staffscale:1,flags:0} +if(grp){staff.flags=grp.flags +grp.flags&=STOP_BAR} +if(parse.state==3&&!curr.newsy){curr.newsy=true +sy=par_sy +par_sy=clone(sy) +sy.next=par_sy +par_sy.voices=clone(par_sy.voices) +par_sy.staves=clone(par_sy.staves)} +st=par_sy.staves.length +par_sy.staves.push(staff) +par_sy.nstaff=st +curr.st_cnv[n]=st}else{staff=par_sy.staves[st]} +def=curr.scoreDef +if(def){for(k in def){switch(k.slice(0,3)){case"cle":case"key":case"met":staff[k]=def[k] +break}}} +for(k in tag){switch(k.slice(0,3)){case"cle":case"key":case"met":staff[k]=tag[k] +break}} +if(tag.label){staff.label=tag}else if(grp&&grp.label){staff.label={label:grp.label} +delete grp.label} +if(tag.spacing){var v=tag.spacing.slice(-1) +if(v>='0'&&v<='9') +staff.maxsep=Number(tag.spacing)/100*CM +else +staff.maxsep=get_unit(tag.spacing)} +if(tag.children) +curr.st=st},staffGrp:function(tag){if(!tag.symbol) +return +var grp=curr.staffGrp +if(!grp){curr.staffGrp=grp={lvl:0,flags:0} +switch(tag.symbol){case"brace":grp.flags|=OPEN_BRACE;break +case"bracket":grp.flags|=OPEN_BRACKET;break}}else{grp.lvl++ +switch(tag.symbol){case"brace":grp.flags|=OPEN_BRACE2;break +case"bracket":grp.flags|=OPEN_BRACKET2;break}} +if(tag["bar.thru"]=="false") +grp.flags|=STOP_BAR},syl:set_text,tempo:function(tag){set_text(tag) +if(!tag.children) +fne[tag.name](tag)},text:function(tag){if(curr.text!=undefined) +curr.text+=tag.content.replace(/[ \t\n]+/g,' ')},tie:function(tag){var m1,m2,not1,not2,s1=get_ref(tag),s2=get_ref(tag,s1),ty=C.SL_AUTO +if(!s1||!s2) +return +switch(tag.curvedir){case"above":ty=C.SL_ABOVE;break +case"below":ty=C.SL_BELOW;break} +switch(tag.lform){case"dashed":case"dotted":ty|=C.SL_DOTTED;break} +if(s1.s){not1=s1 +s1=s1.s +not2=s2 +s2=s2.s +not1.tie_ty=ty +not1.tie_e=not2 +not2.tie_s=not1}else{for(m1=0;m1<=s1.nhd;m1++){not1=s1.notes[m1] +for(m2=0;m2<=s2.nhd;m2++){not2=s2.notes[m2] +if(not1.pit==not2.pit){not1.tie_ty=ty +not1.tie_e=not2 +not2.tie_s=not1}}}} +s1.ti1=s2.ti2=true},trill:function(tag){var s2,s=get_ref(tag) +if(!s) +return +if(tag.dur||tag["dur.ges"]||tag.endid||tag.tstamp2){s2=get_ref(tag,s) +if(!s2) +return} +if(s.s) +s=s.s +switch(tag.place){case"above":s.pos.orn=C.SL_ABOVE;break +case"below":s.pos.orn=C.SL_BELOW;break} +if(!s2){add_dcn(s,"trill") +return} +if(s.time>=s2.time){error(1,null,"Bad trill endings") +return} +add_dcn(s,"trill(") +add_dcn(s2,"trill)")},tuplet:function(tag){curr.tp.push(curvoice.last_sym||true)},tupletSpan:function(tag){var last,next,f,s=get_ref(tag),s2=get_ref(tag,s) +if(!s||!s2) +return +if(s.tp.length){tp_fl(s.tp[0].f,tag) +return} +last=curvoice.last_sym +curvoice.last_sym=s2 +next=s2.next +s2.next=null +tp_set(s,tag) +s2.next=next +curvoice.last_sym=last},verse:function(tag){curr.verse=tag},work:function(tag){curr.work=true},},fne={beam:function(tag){curr.s.beam_end=true +curr.beam=0},chord:function(tag){var id=tag["xml:id"] +if(id) +do_delayed(id,curr.chord) +curr.chord=null},composer:function(tag){if(!curr.text) +return +if(info.C==undefined) +info.C=curr.text +else +info.C+="\n"+curr.text +curr.text=undefined},dir:function(tag){var gch,ty,t,i,font=gene.deffont,s=get_ref(tag) +if(!s) +return +if(s.s) +s=s.s +switch(tag.name){case"dir":case"dynam":switch(tag.place){default:ty="^";break +case"below":ty="_";break} +break +default:ty="g" +switch(tag.place){default:s.pos.gch=C.SL_ABOVE;break +case"below":s.pos.gch=C.SL_BELOW;break} +break} +t=curr.text.split('\n') +if(t[0][0]==' ') +t[0]=t[0].slice(1) +if(!s.a_gch) +s.a_gch=[] +for(i=0;i type "+tag.type+"not treated") +break} +curr.text=undefined},dynam:function(tag){var t,s=get_ref(tag) +if(!s) +return +if(s.s) +s=s.s +t=curr.text.replace(/[ \t\n]+/g,' ') +if(t[0]==' ') +t=t.slice(1) +switch(tag.place){case"above":s.pos.dyn=C.SL_ABOVE;break +case"below":s.pos.dyn=C.SL_BELOW;break} +add_dcn(s,t,tag) +curr.text=undefined},ending:function(tag){var v=voice_tb.length +while(--v>=0){if(voice_tb[v].last_sym) +voice_tb[v].last_sym.rbstop=2}},fing:function(tag){var s=get_ref(tag) +if(!s) +return +if(s.s) +s=s.s +switch(tag.place){case"above":s.pos.orn=C.SL_ABOVE;break +case"below":s.pos.orn=C.SL_BELOW;break} +add_dcn(s,curr.text) +curr.text=undefined},label:function(tag){if(!curr.text) +return +var grp=curr.staffGrp,st=curr.st +if(st==undefined){if(grp){grp.label=curr.text +curr.text=undefined} +return} +var staff=par_sy.staves[st] +if(!staff.label) +staff.label={} +staff.label.label=curr.text +curr.text=undefined},labelAbbr:function(tag){if(!curr.text) +return +var st=curr.st,staff=par_sy.staves[st] +if(!staff.label) +staff.label={} +staff.label["label.abbr"]=curr.text +curr.text=undefined},measure:function(tag){var v,s,s2,s3,p_v,tim,ty,dotted,n=voice_tb.length +if(curr.tp.length){error(1,null,"No end of tuplet") +curr.tp=[]} +if(tag.left){ty=bar_ty[tag.left] +if(ty&&ty[0]=='.'){ty=ty.slice(1) +dotted=true}} +if(ty||curr.ending){for(v=0;vtim) +tim=p_v.time} +if(curr.mrest){curr.mrest=false +for(v=0;v=100){error(1,null,"No voice in staff "+st+" tag:"+tag.name) +return} +for(s=voice_tb[v_rg].last_sym;s;s=s.prev){if(s.time<=tim){if(ref!=0) +break +while(s&&s.type!=C.BAR) +s=s.prev +break}}} +if(!s){error(1,null,"No symbol at beat "+ref+" in staff "+tag.staff+" tag:"+tag.name) +return} +if(!end) +return s +if(s==end){error(1,s,"References on a same element") +if(!s.next) +return +s=s.next} +if(s.time<=end.time){error(0,s,"References going back in time") +return} +return s} +function keycnv(tag){var sig2kM={"0":"C","1s":"G","2s":"D","3s":"A","4s":"E","5s":"B","6s":"F#","7s":"C#","1f":"F","2f":"Bb","3f":"Eb","4f":"Ab","5f":"Db","6f":"Gb","7f":"Cb"},sig2km={"0":"Am","1s":"Em","2s":"Bm","3s":"F#m","4s":"C#m","5s":"G#m","6s":"D#m","7s":"A#m","1f":"Dm","2f":"Gm","3f":"Cm","4f":"Fm","5f":"Bbm","6f":"Ebm","7f":"Abm"} +return tag["key.mode"]!="minor"?sig2kM[tag["key.sig"]]:sig2km[tag["key.sig"]]} +function metercnv(tag){switch(tag["meter.sym"]){case"common":return"C" +case"cut":return"C|"} +if(tag["meter.unit"]) +return tag["meter.count"]+"/"+tag["meter.unit"] +return tag["meter.count"]} +function meteru(tag){var mu=1 +switch(tag["meter.sym"]){case"common":mu=4;break +case"cut":mu=2;break} +if(tag["meter.unit"]) +mu=Number(tag["meter.unit"]) +return C.BLEN/mu} +function set_text(tag){var f +switch(tag.name){case"dir":f="annotation";break +case"dynam":f="dynam";break +case"harm":f="gchord";break +case"syl":f="vocal";break +case"title":f="title";break} +if(f) +set_font(f) +curr.text=""} +function tp_fl(f,tag){if(tag["num.visible"]=="false"){f[0]=1}else{if(tag["bracket.visible"]=="false") +f[1]=2 +if(tag["num.format"]=="ratio") +f[2]=2} +switch(tag["bracket.place"]){case"above":f[3]=1;break +case"below":f[3]=2;break}} +function tp_set(s,tag){var fact,s2,s3,n,nbas=tag.numbase||2,f=Object.create(cfmt.tuplets) +while(s.grace) +s=s.next +if(!s.tp) +s.tp=[] +s.tp.push({p:tag.num,q:nbas,f:f}) +tp_fl(f,tag) +if(tag.dur) +fact=durcnv(tag)/(curvoice.time-s1.time) +else +fact=nbas/tag.num +tp_adj(s,fact) +s2=curvoice.last_sym +if(!s2.dur){for(s3=s2;!s3.dur;s3=s3.prev);s3.tpe=s2.tpe +delete s2.tpe} +n=0 +for(s3=s;s3;s3=s3.next){if(s3.dur) +n++} +s.tp[s.tp.length-1].ro=n} +function unitcnv(v){var nv=v.slice(-1) +if(nv>='0'&&nv<='9') +return(v*curr.pt).toString() +return v} +function sanitize(){var i,v,used_st=[] +for(v=0;v0;i--){if(used_st[i]) +break} +if(i!=par_sy.nstaff) +error(1,null,"Unused staves "+(i+1)+".."+par_sy.nstaff) +par_sy.nstaff=i} +function parse_mei(tag){if(fns[tag.name]){parse.istart=tag.ix +if(fns[tag.name](tag)) +return} +if(!tag.children) +return +var i,n=tag.children.length +for(i=0;i') +i++ +continue} +l++} +return l} +function build_cell(c){var i,j,k,u='',t='' +row+=' ' +if(c.length==1){row+=c[0] +return} +i=0 +while(i<4){t+=c[i] +k=get_l(c[i]) +j=k +while(--j>=0) +u+=' ' +if(i<3&&!c[i+1]){while(i<3&&!c[i+1]) +i++ +i++ +if(i<4&&c[i]) +t+=' '}else{j=k +t+='' +while(--j>=0) +t+='_' +t+='' +i++ +if(i<4&&c[i]) +t+=' ' +else +t+=' '}} +row+=t+'' ++u ++''} +set_chords() +if(!nns.ls){cells=chords}else{bar=bars +bars=[] +for(i=0;icells.length) +nc=cells.length +abc.set_font('nns') +x=img.lm+30 +y=-1+font.size*.6 +nr=0 +hr=font.size*2 +for(i=0;i=nc){if(row){abc.out_svg(''+row+'\n')} +row='' +y-=hr +k=0 +nr++ +if(parts[i]){w=font.size*parts[i].length*.6+10 +if(w<50) +w=50 +abc.out_svg(''+parts[i]+'\n')}} +k++ +if(bars[i].slice(-1)==':') +row+=' |:' +build_cell(cells[i]) +if(bars[i+1][0]==':') +row+=' :|'} +if(row){abc.out_svg(''+row+'\n')} +abc.vskip(hr*nr+6)} +var p_voice,n,font,f2 +abc.set_page() +img=abc.get_img() +if(!cfmt.nnsfont) +abc.param_set_font("nnsfont","monospace 16") +font=abc.get_font('nns') +abc.blk_flush() +build_nns(s,font) +abc.blk_flush()},set_stems:function(of){var C,tsfirst,voice_tb,fmt,p_v,s,s2,abc=this,nns=abc.cfmt().nns +function get_beat(s){var beat=C.BLEN/4 +if(!s.a_meter[0]||s.a_meter[0].top[0]=='C'||!s.a_meter[0].bot) +return beat +beat=C.BLEN/s.a_meter[0].bot[0]|0 +if(s.a_meter[0].bot[0]==8&&s.a_meter[0].top[0]%3==0) +beat=C.BLEN/8*3 +return beat} +function set_nm(p,tr,mode){var i,o,o2,a,n,csa=[] +i=p.indexOf('/') +if(i>0){while(1){if(p[i-1]!='<') +break +i=p.indexOf('/',i+1) +if(i<0) +break}} +if(i<0){csa.push(p)}else{csa.push(p.slice(0,i)) +csa.push(p.slice(i+1))} +for(i=0;icur_beat){if(beat_i<3) +beat_i++ +cur_beat+=beat} +if(s.part) +parts[chords.length]=s.part.text +switch(s.type){case C.KEY:tr=(s.k_sf+12)*5 +mode=s.k_mode +break +case C.NOTE:case C.REST:if(!s.a_gch) +break +for(i=0;i=0) +rep=true +while(s.ts_next&&s.ts_next.type==C.BAR) +s=s.ts_next +break +case C.METER:beat=get_beat(s) +wm=s.wmeasure +break}} +if(chord.length){bars.push('') +chords.push(chord)} +if(!chords.length) +return +sb.chords=chords +sb.bars=bars +if(parts.length) +sb.parts=parts} +if(nns){C=abc2svg.C +tsfirst=this.get_tsfirst() +fmt=tsfirst.fmt +voice_tb=this.get_voice_tb() +p_v=voice_tb[this.get_top_v()] +s={type:C.BLOCK,subtype:'nns',dur:0,time:0,p_v:p_v,v:p_v.v,st:p_v.st} +build_chords(s) +if(!s.chords){}else if(nns.nomusic){this.set_tsfirst(s)}else if(nns.n<0){for(s2=tsfirst;s2.ts_next;s2=s2.ts_next);s.time=s2.time +s.prev=p_v.last_sym.prev +s.prev.next=s +s.next=p_v.last_sym +p_v.last_sym.prev=s +s.ts_prev=s2.ts_prev +s.ts_prev.ts_next=s +s.ts_next=s2 +s2.ts_prev=s +if(s2.seqst){s.seqst=true +s2.seqst=false}}else{s.next=p_v.sym +s.ts_next=tsfirst +tsfirst.ts_prev=s +this.set_tsfirst(s) +p_v.sym.prev=s +p_v.sym=s} +s.fmt=s.prev?s.prev.fmt:fmt} +of()},set_fmt:function(of,cmd,parm){if(cmd=="nns"){if(!parm) +parm="1" +parm=parm.split(/\s+/) +var nns={n:Number(parm.shift())} +if(isNaN(nns.n)){if(parm.length){this.syntax(1,this.errs.bad_val,"%%nns") +return} +nns.n=1} +while(parm.length){var item=parm.shift() +if(item=="nomusic") +nns.nomusic=true +else if(item=="roman") +nns.roman=1 +else if(item=="repbrk") +nns.repbrk=true +else if(item.slice(0,8)=="include=") +nns.ls=item.slice(8).split(',') +else if(item.slice(0,-1)=="roman=") +nns.roman=item.slice(-1)=="1"?1:2} +this.cfmt().nns=nns +return} +of(cmd,parm)},set_hooks:function(abc){abc.block_gen=abc2svg.nns.block_gen.bind(abc,abc.block_gen) +abc.set_stems=abc2svg.nns.set_stems.bind(abc,abc.set_stems) +abc.set_format=abc2svg.nns.set_fmt.bind(abc,abc.set_format)}} +if(!abc2svg.mhooks) +abc2svg.mhooks={} +abc2svg.mhooks.nns=abc2svg.nns.set_hooks diff --git a/lib/ChordPro/res/abc/abc2svg/page-1.js b/lib/ChordPro/res/abc/abc2svg/page-1.js new file mode 100644 index 00000000..397d14cc --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/page-1.js @@ -0,0 +1,252 @@ +//page.js-module to generate pages +if(typeof abc2svg=="undefined") +var abc2svg={} +abc2svg.page={abc_end:function(of){var page=this.page +if(page&&page.in_page) +abc2svg.page.close_page(page) +if(abc2svg.page.user_out){user.img_out=abc2svg.page.user_out +abc2svg.page.user_out=null} +of()},svg_tag:function(w,h,ty){w=Math.ceil(w) +h=Math.ceil(h) +abc2svg.page.user_out('')},gen_hf:function(page,ty){var a,i,j,k,x,y,y0,s,str,font=page.abc.get_font(ty.substr(0,6)),cfmt=page.abc.cfmt(),fh=font.size*1.1,pos=['">','" text-anchor="middle">','" text-anchor="end">'] +function clean_txt(txt){return txt.replace(/<|>|&.*?;|&/g,function(c){switch(c){case'<':return"<" +case'>':return">" +case'&':return"&"} +return c})} +function clr(str){return str.indexOf('\u00ff')>=0?'':str} +function header_footer(o_font,str){var c,d,i,k,t,n_font,s,c_font=o_font,nl=1,j=0,r=["","",""] +if(str[0]=='"') +str=str.slice(1,-1) +while(1){i=str.indexOf('$',j) +if(i<0) +break +c=str[++i] +s='$'+c +switch(c){case'd':if(!abc2svg.get_mtime) +break +d=abc2svg.get_mtime(abc.parse.fname) +case'D':if(c=='D') +d=new Date() +if(cfmt.dateformat[0]=='"') +cfmt.dateformat=cfmt.dateformat.slice(1,-1) +d=strftime(cfmt.dateformat,d) +break +case'F':d=typeof document!="undefined"?window.location.href:page.abc.parse.fname +break +case'I':c=str[++i] +s+=c +case'T':t=page.abc.info()[c] +d=t?t.split('\n',1)[0]:'' +break +case'P':case'Q':t=c=='P'?page.pn:page.pna +switch(str[i+1]){case'0':s+='0' +d=(t&1)?'\u00ff':t +break +case'1':s+='1' +d=(t&1)?t:'\u00ff' +break +default:d=t +break} +break +case'V':d="abc2svg-"+abc2svg.version +break +default:d='' +if(c=='0') +n_font=o_font +else if(c>='1'&&c<'9') +n_font=page.abc.get_font("u"+c) +else +break +if(n_font==c_font) +break +if(c_font!=o_font) +d+="" +c_font=n_font +if(c_font==o_font) +break +d+='' +break} +str=str.replace(s,d) +j=i} +if(c_font!=o_font) +str+="";str=str.split('\n') +r[4]=str.length +for(j=0;j=0) +s=str.slice(k,j) +else +s=str.slice(k) +if(s) +page.hf+='') +page.in_page=true +ht+=page.topmargin +page.hmax=cfmt.pageheight-page.botmargin-ht +page.hf='' +if(page.header){l=abc.get_font_style().length +h=abc2svg.page.gen_hf(page,"header") +if(!h&&page.pn==1&&page.header1) +h=abc2svg.page.gen_hf(page,"header1") +sty=abc.get_font_style().slice(l) +if(cfmt.fullsvg||sty!=page.hsty){page.hsty=sty +sty='\n'}else{sty=''} +abc2svg.page.svg_tag(cfmt.pagewidth,ht+h,"header") +abc2svg.page.user_out(sty+'\n'+ +page.hf+'\n') +page.hmax-=h;page.hf=''}else{abc2svg.page.svg_tag(cfmt.pagewidth,ht,"header") +abc2svg.page.user_out('\n')} +if(page.footer){l=abc.get_font_style().length +page.fh=abc2svg.page.gen_hf(page,"footer") +sty=abc.get_font_style().slice(l) +if(cfmt.fullsvg||sty!=page.fsty){page.fsty=sty +page.ffsty='\n'}else{page.ffsty=''} +page.hmax-=page.fh} +page.h=0},close_page:function(page){var h,cfmt=page.abc.cfmt() +page.in_page=false +if(page.footer){h=page.hmax+page.fh-page.h +abc2svg.page.svg_tag(cfmt.pagewidth,h,"footer") +abc2svg.page.user_out(page.ffsty+'\n'+ +page.hf+'\n')} +abc2svg.page.user_out('
') +page.h=0},img_in:function(p){var h,ht,nh,page=this.page +function blkcpy(page){while(page.blk.length) +abc2svg.page.user_out(page.blk.shift()) +page.blk=null} +switch(p.slice(0,4)){case"0||(page.oneperpage&&this.info().X)||!page.h){if(page.in_page) +abc2svg.page.close_page(page) +abc2svg.page.open_page(page,0)} +page.blk=[] +page.hb=page.h +break +case"=page.hmax){ht=page.blk?0:this.cfmt().topspace +if(page.blk){if(!page.hb){blkcpy(page) +nh=0}else{nh=page.h-page.hb +page.h=page.hb}} +abc2svg.page.close_page(page) +abc2svg.page.open_page(page,ht) +if(page.blk){blkcpy(page) +page.h=nh} +if(h>page.hmax) +break} +if(page.blk) +page.blk.push(p) +else +abc2svg.page.user_out(p) +page.h+=h +break +case"1&&isLeapYear())?1:0),3),'%k':nHour,'%l':(nHour+11)%12+1,'%m':zeroPad(nMonth+1,2),'%n':nMonth+1,'%M':zeroPad(date.getMinutes(),2),'%p':(nHour<12)?'AM':'PM','%P':(nHour<12)?'am':'pm','%s':Math.round(date.getTime()/1000),'%S':zeroPad(date.getSeconds(),2),'%u':nDay||7,'%V':(function(){var target=getThursday(),n1stThu=target.valueOf();target.setMonth(0,1);var nJan1=target.getDay();if(nJan1!==4)target.setMonth(0,1+((4-nJan1)+7)%7);return zeroPad(1+Math.ceil((n1stThu-target)/604800000),2)})(),'%w':nDay,'%x':date.toLocaleDateString(),'%X':date.toLocaleTimeString(),'%y':(nYear+'').slice(2),'%Y':nYear,'%z':date.toTimeString().replace(/.+GMT([+-]\d+).+/,'$1'),'%Z':date.toTimeString().replace(/.+\((.+?)\)$/,'$1')}[sMatch]||'')+'')||sMatch})} diff --git a/lib/ChordPro/res/abc/abc2svg/pedline-1.js b/lib/ChordPro/res/abc/abc2svg/pedline-1.js new file mode 100644 index 00000000..12d3c736 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/pedline-1.js @@ -0,0 +1,36 @@ +//pedline.js-module to draw pedal lines instead of'Ped .. *' +if(typeof abc2svg=="undefined") +var abc2svg={} +abc2svg.pedline={draw_all_deco:function(of){var de,i,x,dp,ds,a_de=this.a_de() +if(!a_de.length) +return +if(this.cfmt().pedline){for(i=0;idp.y) +de.y=dp.y +dp.y=de.y}else{de.x=ds.s.x-8 +if(!de.defl.noen) +de.val=de.s.x-ds.s.x-de.s.wl}}} +of()},out_lped:function(of,x,y,val,defl){if(!this.cfmt().pedline){of(x,y,val,defl) +return} +this.xypath(x,y+8) +if(defl.nost){if(defl.nost==2){this.out_svg("l2.5 6") +val-=2.5}else{this.out_svg("m0 6")}}else{this.out_svg("v6")} +if(defl.noen){if(defl.noen==2){val-=2.5 +this.out_svg("h"+val.toFixed(1)+'l2.5 -6')}else{this.out_svg("h"+val.toFixed(1))}}else{this.out_svg("h"+val.toFixed(1)+'v-6')} +this.out_svg('"/>\n')},set_fmt:function(of,cmd,param){if(cmd=="pedline") +this.cfmt().pedline=this.get_bool(param) +else +of(cmd,param)},set_hooks:function(abc){abc.draw_all_deco=abc2svg.pedline.draw_all_deco.bind(abc,abc.draw_all_deco) +abc.out_lped=abc2svg.pedline.out_lped.bind(abc,abc.out_lped) +abc.set_format=abc2svg.pedline.set_fmt.bind(abc,abc.set_format)}} +if(!abc2svg.mhooks) +abc2svg.mhooks={} +abc2svg.mhooks.pedline=abc2svg.pedline.set_hooks diff --git a/lib/ChordPro/res/abc/abc2svg/perc-1.js b/lib/ChordPro/res/abc/abc2svg/perc-1.js new file mode 100644 index 00000000..409305d7 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/perc-1.js @@ -0,0 +1,49 @@ +//perc.js-module to handle%%percmap +if(typeof abc2svg=="undefined") +var abc2svg={} +abc2svg.perc={do_perc:function(parm){var pits=new Int8Array([0,0,1,2,2,3,3,4,5,5,6,6]),accs=new Int8Array([3,1,3,-1,3,3,1,3,-1,3,-1,3]) +var prn={"a-b-d":35,"a-s":38,"b-d-1":36,"ca":69,"cl":75,"co":56,"c-c":52,"c-c-1":49,"c-c-2":57,"c-h-h":42,"e-s":40,"h-a":67,"h-b":60,"h-c":39,"h-f-t":43,"h-m-t":48,"h-ti":65,"h-to":50,"h-w-b":76,"l-a":68,"l-b":61,"l-c":64,"l-f-t":41,"l-g":74,"l-m-t":47,"l-ti":66,"l-to":45,"l-w":72,"l-w-b":77,"m":70,"m-c":78,"m-h-c":62,"m-t":80,"o-c":79,"o-h-c":63,"o-h-h":46,"o-t":81,"p-h-h":44,"r-b":53,"r-c-1":51,"r-c-2":59,"s-c":55,"s-g":73,"s-s":37,"s-w":71,"t":54,"v":58} +function toabc(p){var i,j,s,pit +if(/^[_^]*[A-Ga-g][,']*$/.test(p)) +return p +pit=Number(p) +if(isNaN(pit)){p=p.toLowerCase(p);s=p[0];i=0 +while(1){j=p.indexOf('-',i) +if(j<0) +break +i=j+1;s+='-'+p[i]} +pit=prn[s] +if(!pit){switch(p[0]){case'c':switch(p[1]){case'a':pit=prn.ca;break +case'l':pit=prn.cl;break +case'o':pit=prn.co;break} +break +case'h':case'l':i=p.indexOf('-') +if(p[i+1]!='t') +break +switch(p[i+2]){case'i':case'o':pit=prn[s+p[i+2]] +break} +break} +if(!pit) +return}} +p=["C","^C","D","_E","E","F","^F","G","^G","A","_B","B"][pit%12] +while(pit<60){p+=',' +pit+=12} +while(pit>=72){p+="'" +pit-=12} +return p} +var a=parm.split(/\s+/),p=a[1].replace(/[=_^]/,'') +this.do_pscom("map MIDIdrum "+a[1]+" play="+toabc(a[2])+" print="+p+ +(a[3]?(" heads="+a[3]):'')) +this.set_v_param("perc","MIDIdrum")},set_perc:function(a){var i,item,s,curvoice=this.get_curvoice() +for(i=0;i=out.length) +o=-1} +if(!res||o<0){if(conf.onend) +conf.onend() +return}} +current=out[o]=='sf2'?audio5:midi5;abcplay.play=current.play;abcplay.stop=current.stop +if(current.set_output) +current.set_output(out[o]);if(abc2svg.pwait){if(typeof abc2svg.pwait=="boolean"){abc2svg.pwait=function(){abcplay.play(init.istart,init.i_iend,init.a_e)}} +return} +abcplay.play(init.istart,init.i_iend,init.a_e)} +conf.gain=0.7;conf.speed=1;(function(){var v +try{if(!localStorage) +return}catch(e){return} +if(!conf.sfu){v=localStorage.getItem("sfu") +if(v) +conf.sfu=v} +v=localStorage.getItem("volume") +if(v) +conf.gain=Number(v)})() +if(typeof Midi5=="function") +midi5=Midi5(conf) +if(typeof Audio5=="function") +audio5=Audio5(conf);return abcplay} +if(typeof module=='object'&&typeof exports=='object') +exports.AbcPlay=AbcPlay +function ToAudio(){var C=abc2svg.C,a_e,p_time,abc_time,play_factor +return{clear:function(){var a_pe=a_e;a_e=null +return a_pe},add:function(start,voice_tb){var i,n,dt,d,v,rep_st_s,rep_en_s,rep_nx_s,rep_st_fac,instr=[],s=start +function set_voices(){var v,p_v,s,mi +a_e.push(new Float32Array([0,0,-1,121,0,1,0])) +for(v=0;v=0){note=s.notes[i] +if(note.b40==b40){note.ti2=true +d+=s.dur/play_factor;return note.tie_ty?do_tie(s,b40,d):d}} +return d} +function gen_grace(s){var g,i,n,t,d,s2,next=s.next +if(s.sappo){d=C.BLEN/16}else if((!next||next.type!=C.NOTE)&&s.prev&&s.prev.type==C.NOTE){d=s.prev.dur/2}else{next.ts_prev.ts_next=next.ts_next;next.ts_next.ts_prev=next.ts_prev;for(s2=next.ts_next;s2;s2=s2.ts_next){if(s2.time!=next.time){next.ts_next=s2 +next.ts_prev=s2.ts_prev;next.ts_prev.ts_next=next;s2.ts_prev=next +break}} +d=next.dur/12 +if(d&(d-1)==0) +d=next.dur/2 +else +d=next.dur/3;next.time+=d;next.dur-=d} +n=0 +for(g=s.extra;g;g=g.next) +if(g.type==C.NOTE) +n++;d/=n*play_factor;t=p_time +for(g=s.extra;g;g=g.next){if(g.type!=C.NOTE) +continue +gen_notes(g,t,d);t+=d}} +function gen_notes(s,t,d){for(var i=0;i<=s.nhd;i++){var note=s.notes[i] +if(note.ti2) +continue +a_e.push(new Float32Array([s.istart,t,instr[s.v],note.midi,note.tie_ty?do_tie(s,note.b40,d):d,1,s.v]))}} +if(!a_e){a_e=[] +abc_time=p_time=0;play_factor=C.BLEN/4*120/60}else if(s.time0){p_time+=dt/play_factor;abc_time=s.time} +switch(s.type){case C.BAR:if(!s.seqst) +break +if(s==rep_en_s){s=rep_nx_s +abc_time=s.time}else if(s.bar_type[0]==':'){rep_nx_s=s +if(!rep_en_s) +rep_en_s=s +if(rep_st_s){s=rep_st_s +play_factor=rep_st_fac}else{s=start;set_voices()} +abc_time=s.time +break} +if(s.bar_type[s.bar_type.length-1]==':'){rep_st_s=s;rep_en_s=null +rep_st_fac=play_factor}else if(s.text&&s.text[0]=='1'){rep_en_s=s} +break +case C.GRACE:if(s.time==0&&abc_time==0){dt=0 +if(s.sappo) +dt=C.BLEN/16 +else if(!s.next||s.next.type!=C.NOTE) +dt=d/2;abc_time-=dt} +gen_grace(s) +break +case C.REST:case C.NOTE:d=s.dur +if(s.next&&s.next.type==C.GRACE){dt=0 +if(s.next.sappo) +dt=C.BLEN/16 +else if(!s.next.next||s.next.next.type!=C.NOTE) +dt=d/2;s.next.time-=dt;d-=dt} +d/=play_factor +if(s.type==C.NOTE) +gen_notes(s,p_time,d) +else +a_e.push(new Float32Array([s.istart,p_time,0,0,d,0,s.v])) +break +case C.BLOCK:switch(s.subtype){case"midictl":a_e.push(new Float32Array([s.istart,p_time,-1,s.ctrl,s.val,1,s.v])) +break +case"midiprog":instr[s.v]=s.instr +break} +break} +s=s.ts_next}}}} +if(typeof module=='object'&&typeof exports=='object') +exports.ToAudio=ToAudio +var abcsf2=[] +function Audio5(i_conf){var conf=i_conf,onend=function(){},onnote=function(){},errmsg,ac,gain,params=[],rates=[],w_instr=0,evt_idx,iend,stime,timouts=[] +function js_instr_load(instr,done,fail){abc2svg.loadjs(conf.sfu+'/'+instr+'.js',function(){done(b64dcod(abcsf2[instr]))},fail)} +if(!conf.instr_load) +conf.instr_load=js_instr_load +var b64d=[] +function init_b64d(){var b64l='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',l=b64l.length +for(var i=0;i>16)&0xff;a[j++]=(t>>8)&0xff;a[j++]=t&0xff} +if(l!=s.length){t=(b64d[s[i]]<<18)+ +(b64d[s[i+1]]<<12)+ +(b64d[s[i+2]]<<6)+ +b64d[s[i+3]];a[j++]=(t>>16)&0xff +if(j>8)&0xff} +return a} +function sample_cp(b,s){var i,n,a=b.getChannelData(0) +for(i=0;i=.4) +parm.sustain=0.01 +else +parm.sustain=1-parm.sustain/.4 +sample_cp(parm.buffer,sample) +if(gen.sampleModes&&(gen.sampleModes.amount&1)){parm.loopStart=parser.sampleHeader[sid].startLoop/sampleRate;parm.loopEnd=parser.sampleHeader[sid].endLoop/sampleRate} +var scale=(gen.scaleTuning?gen.scaleTuning.amount:100)/100,tune=(gen.coarseTune?gen.coarseTune.amount:0)+ +(gen.fineTune?gen.fineTune.amount:0)/100+ +parser.sampleHeader[sid].pitchCorrection/100- +(gen.overridingRootKey?gen.overridingRootKey.amount:parser.sampleHeader[sid].originalPitch) +for(j=gen.keyRange.lo;j<=gen.keyRange.hi;j++){rates[instr][j]=Math.pow(Math.pow(2,1/12),(j+tune)*scale);params[instr][j]=parm}}} +function load_instr(instr,a_e){w_instr++;conf.instr_load(instr,function(sf2_bin){var parser=new sf2.Parser(sf2_bin);parser.parse();sf2_create(parser,instr);if(--w_instr==0) +play_start(a_e)},function(){errmsg('could not find the instrument '+ +((instr/128)|0).toString()+'-'+ +(instr%128).toString());if(--w_instr==0) +play_start(a_e)})} +function load_res(a_e){var i,e,instr,v,bk=[] +for(i=evt_idx;i=iend){onend() +return} +if(conf.new_speed){stime=ac.currentTime- +(ac.currentTime-stime)*conf.speed/conf.new_speed;conf.speed=conf.new_speed;conf.new_speed=0} +timouts=[];t=e[1]/conf.speed;maxt=t+3 +while(1){d=e[4]/conf.speed +if(e[2]>=0){if(e[5]!=0) +note_run(e,t+stime,d) +var i=e[0];st=(t+stime-ac.currentTime)*1000;timouts.push(setTimeout(onnote,st,i,true));setTimeout(onnote,st+d*1000,i,false)} +e=a_e[++evt_idx] +if(!e||evt_idx>=iend){setTimeout(onend,(t+stime-ac.currentTime+d)*1000) +return} +t=e[1]/conf.speed +if(t>maxt) +break} +timouts.push(setTimeout(play_next,(t+stime-ac.currentTime)*1000-300,a_e))} +function play_start(a_e){if(iend==0){onend() +return} +if(w_instr!=0) +return +gain.connect(ac.destination);stime=ac.currentTime+.2 +-a_e[evt_idx][1]*conf.speed;play_next(a_e)} +init_b64d();if(!conf.sfu) +conf.sfu="Scc1t2" +return{get_outputs:function(){return(window.AudioContext||window.webkitAudioContext)?['sf2']:null},play:function(istart,i_iend,a_e){if(!a_e||istart>=a_e.length){onend() +return} +if(conf.onend) +onend=conf.onend +if(conf.onnote) +onnote=conf.onnote +errmsg=conf.errmsg||alert +function play_unlock(){var buf=ac.createBuffer(1,1,22050),src=ac.createBufferSource();src.buffer=buf;src.connect(ac.destination);src.noteOn(0)} +if(!gain){ac=conf.ac +if(!ac){conf.ac=ac=new(window.AudioContext||window.webkitAudioContext);if(/iPad|iPhone|iPod/.test(navigator.userAgent)) +play_unlock()} +gain=ac.createGain();gain.gain.value=conf.gain} +iend=i_iend;evt_idx=istart;load_res(a_e);play_start(a_e)},stop:function(){iend=0 +timouts.forEach(function(id){clearTimeout(id)}) +play_next() +if(gain){gain.disconnect();gain=null}},set_vol:function(v){if(gain) +gain.gain.value=v}}} +(function(root,factory){if(typeof exports==="object"){root.sf2=exports;factory(exports)}else if(typeof define==="function"&&define.amd){define(["exports"],function(exports){root.sf2=exports;return(root.sf2,factory(exports))})}else{root.sf2={};factory(root.sf2)}}(this,function(sf2){"use strict";sf2.Parser=function(input,options){options=options||{};this.input=input;this.parserOptions=options.parserOptions};sf2.Parser.prototype.parse=function(){var parser=new sf2.Riff.Parser(this.input,this.parserOptions),chunk;parser.parse();if(parser.chunkList.length!==1) +throw new Error('wrong chunk length');chunk=parser.getChunk(0);if(chunk===null) +throw new Error('chunk not found');this.parseRiffChunk(chunk);this.input=null};sf2.Parser.prototype.parseRiffChunk=function(chunk){var parser,data=this.input,ip=chunk.offset,signature;if(chunk.type!=='RIFF') +throw new Error('invalid chunk type:'+chunk.type);signature=String.fromCharCode(data[ip++],data[ip++],data[ip++],data[ip++]);if(signature!=='sfbk') +throw new Error('invalid signature:'+signature);parser=new sf2.Riff.Parser(data,{'index':ip,'length':chunk.size-4});parser.parse();if(parser.getNumberOfChunks()!==3) +throw new Error('invalid sfbk structure');this.parseInfoList(parser.getChunk(0));this.parseSdtaList(parser.getChunk(1));this.parsePdtaList(parser.getChunk(2))};sf2.Parser.prototype.parseInfoList=function(chunk){var parser,data=this.input,ip=chunk.offset,signature;if(chunk.type!=='LIST') +throw new Error('invalid chunk type:'+chunk.type);signature=String.fromCharCode(data[ip++],data[ip++],data[ip++],data[ip++]);if(signature!=='INFO') +throw new Error('invalid signature:'+signature);parser=new sf2.Riff.Parser(data,{'index':ip,'length':chunk.size-4});parser.parse()};sf2.Parser.prototype.parseSdtaList=function(chunk){var parser,data=this.input,ip=chunk.offset,signature;if(chunk.type!=='LIST') +throw new Error('invalid chunk type:'+chunk.type);signature=String.fromCharCode(data[ip++],data[ip++],data[ip++],data[ip++]);if(signature!=='sdta') +throw new Error('invalid signature:'+signature);parser=new sf2.Riff.Parser(data,{'index':ip,'length':chunk.size-4});parser.parse();if(parser.chunkList.length!==1) +throw new Error('TODO');this.samplingData=parser.getChunk(0)};sf2.Parser.prototype.parsePdtaList=function(chunk){var parser,data=this.input,ip=chunk.offset,signature;if(chunk.type!=='LIST') +throw new Error('invalid chunk type:'+chunk.type);signature=String.fromCharCode(data[ip++],data[ip++],data[ip++],data[ip++]);if(signature!=='pdta') +throw new Error('invalid signature:'+signature);parser=new sf2.Riff.Parser(data,{'index':ip,'length':chunk.size-4});parser.parse();if(parser.getNumberOfChunks()!==9) +throw new Error('invalid pdta chunk');this.parsePhdr((parser.getChunk(0)));this.parsePbag((parser.getChunk(1)));this.parsePmod((parser.getChunk(2)));this.parsePgen((parser.getChunk(3)));this.parseInst((parser.getChunk(4)));this.parseIbag((parser.getChunk(5)));this.parseImod((parser.getChunk(6)));this.parseIgen((parser.getChunk(7)));this.parseShdr((parser.getChunk(8)))};sf2.Parser.prototype.parsePhdr=function(chunk){var data=this.input,ip=chunk.offset,presetHeader=this.presetHeader=[],size=chunk.offset+chunk.size;if(chunk.type!=='phdr') +throw new Error('invalid chunk type:'+chunk.type);while(ip>>0,genre:(data[ip++]|(data[ip++]<<8)|(data[ip++]<<16)|(data[ip++]<<24))>>>0,morphology:(data[ip++]|(data[ip++]<<8)|(data[ip++]<<16)|(data[ip++]<<24))>>>0})}};sf2.Parser.prototype.parsePbag=function(chunk){var data=this.input,ip=chunk.offset,presetZone=this.presetZone=[],size=chunk.offset+chunk.size;if(chunk.type!=='pbag') +throw new Error('invalid chunk type:'+chunk.type);while(ip>24;sampleLink=data[ip++]|(data[ip++]<<8);sampleType=data[ip++]|(data[ip++]<<8);var sample=new Int16Array(new Uint8Array(data.subarray(this.samplingData.offset+start*2,this.samplingData.offset+end*2)).buffer);startLoop-=start;endLoop-=start;if(sampleRate>0){var adjust=this.adjustSampleData(sample,sampleRate);sample=adjust.sample;sampleRate*=adjust.multiply;startLoop*=adjust.multiply;endLoop*=adjust.multiply} +samples.push(sample);sampleHeader.push({sampleName:sampleName,startLoop:startLoop,endLoop:endLoop,sampleRate:sampleRate,originalPitch:originalPitch,pitchCorrection:pitchCorrection,sampleLink:sampleLink,sampleType:sampleType})}};sf2.Parser.prototype.adjustSampleData=function(sample,sampleRate){var newSample,i,il,j,multiply=1;while(sampleRate<22050){newSample=new Int16Array(sample.length*2);for(i=j=0,il=sample.length;i>16,lo:data[ip++],hi:data[ip++]}})}else{switch(key){case'keyRange':case'velRange':case'keynum':case'velocity':output.push({type:key,value:{lo:data[ip++],hi:data[ip++]}});break;default:output.push({type:key,value:{amount:data[ip++]|(data[ip++]<<8)<<16>>16}});break}} +ip+=2;ip+=2} +return output};sf2.Parser.prototype.parseGenerator=function(chunk){var data=this.input,ip=chunk.offset,size=chunk.offset+chunk.size,code,key,output=[];while(ip>16,lo:data[ip++],hi:data[ip++]}});continue} +switch(key){case'keynum':case'keyRange':case'velRange':case'velocity':output.push({type:key,value:{lo:data[ip++],hi:data[ip++]}});break;default:output.push({type:key,value:{amount:data[ip++]|(data[ip++]<<8)<<16>>16}});break}} +return output};sf2.Parser.prototype.getPresets=function(){var preset=this.presetHeader,zone=this.presetZone,output=[],bagIndex,bagIndexEnd,zoneInfo,presetGenerator,presetModulator,i,il,j,jl +for(i=0,il=preset.length;i=iend||!e){onend() +return} +if(conf.new_speed){stime=window-performance.now()- +(window.performance.now()-stime)*conf.speed/conf.new_speed;conf.speed=conf.new_speed;conf.new_speed=0} +timouts=[];t=e[1]/conf.speed*1000;maxt=t+3000 +while(1){d=e[4]/conf.speed*1000 +if(e[2]>=0){if(e[5]!=0) +note_run(e,t+stime,d) +st=t+stime-window.performance.now();timouts.push(setTimeout(onnote,st,e[0],true));setTimeout(onnote,st+d,e[0],false)}else{c=e[6]&0x0f +op.send(new Uint8Array([0xb0+c,e[3],e[4]]),t+stime) +if(bk[c]==undefined) +bk[c]=0 +switch(e[3]){case 0:bk[c]=(bk[c]&0x7f)|(e[4]<<7) +break +case 32:bk[c]=(bk[c]&0x3f80)|e[4] +break +case 121:bk=[] +break}} +e=a_e[++evt_idx] +if(!e||evt_idx>=iend){setTimeout(onend,t+stime-window.performance.now()+d) +return} +t=e[1]/conf.speed*1000 +if(t>maxt) +break} +timouts.push(setTimeout(play_next,(t+stime-window.performance.now()) +-300,a_e))} +function send_outputs(access){var o,os,out=[];Midi5.ma=access;if(access&&access.outputs.size>0){os=access.outputs.values() +while(1){o=os.next() +if(!o||o.done) +break +out.push(o.value.name)}} +rf(out)} +return{get_outputs:function(f){if(!navigator.requestMIDIAccess){f() +return} +rf=f;navigator.requestMIDIAccess({sysex:true}).then(send_outputs,function(msg){navigator.requestMIDIAccess().then(send_outputs,function(msg){rf()})})},set_output:function(name){var o,os +if(!Midi5.ma) +return +os=Midi5.ma.outputs.values() +while(1){o=os.next() +if(!o||o.done) +break +if(o.value.name==name){op=o.value +break}}},play:function(istart,i_iend,a_e){if(!a_e||istart>=a_e.length){onend() +return} +if(conf.onend) +onend=conf.onend +if(conf.onnote) +onnote=conf.onnote;iend=i_iend;evt_idx=istart;if(0){op.send(new Uint8Array([0xf0,0x7f,0x7f,0x08,0x02,0x00,0x01,0x69,0x69,0x00,0,0xf7]),t)} +v_i=[];stime=window.performance.now()+200 +-a_e[evt_idx][1]*conf.speed*1000;play_next(a_e)},stop:function(){iend=0 +timouts.forEach(function(id){clearTimeout(id)}) +play_next() +if(op&&op.clear) +op.clear()}}} +function follow(abc,user,playconf){var keep_types={note:true,rest:true} +user.anno_stop=function(type,start,stop,x,y,w,h){if(!keep_types[type]) +return +abc.out_svg('\n')} +playconf.onnote=function(i,on){var b,x,y,i,e,elts +if(abc2svg.mu) +elts=abc2svg.mu.d.getElementsByClassName('_'+i+'_') +else +elts=document.getElementsByClassName('_'+i+'_') +if(!elts||!elts.length) +return +e=elts[0] +e.style.fillOpacity=on?0.4:0 +if(on&&!window.no_scroll){b=e.getBoundingClientRect() +if(b.top<0) +y=window.scrollY+b.top- +window.innerHeight/2 +else if(b.bottom>window.innerHeight) +y=window.scrollY+b.bottom+ +window.innerHeight/2 +if(b.left<0) +x=window.scrollX+b.left- +window.innerWidth/2 +else if(b.right>window.innerWidth) +x=window.scrollX+b.right+ +window.innerWidth/2 +if(x!=undefined||y!=undefined) +window.scrollTo(x||0,y||0)}}} +(function(){var sty=document.createElement("style") +sty.innerHTML=".abcr {fill: #d00000; fill-opacity: 0; z-index: 15}" +document.head.appendChild(sty)})() \ No newline at end of file diff --git a/lib/ChordPro/res/abc/abc2svg/psvg-1.js b/lib/ChordPro/res/abc/abc2svg/psvg-1.js new file mode 100644 index 00000000..665bbec7 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/psvg-1.js @@ -0,0 +1,392 @@ +//wps.js +function isQuoted(V){return V.q} +function quote(V){V.q=true;return V} +function unquote(V){delete V.q;return V} +function Symbol(N){this.nm=N;return this} +function isSymbol(V){return V&&V.constructor===Symbol} +function symbolName(V){return V.nm} +function isArray(V){return V&&V.constructor===Array} +function inDs(Ds,K){for(var I=Ds.length-1;0<=I;--I){if("undefined"!=typeof Ds[I][K]) +return Ds[I]} +return false} +function member(C,L){return 0<=L.indexOf(C)} +function PsParser(){var Self=this;function init(L){Self.L=L;Self.N=L.length;Self.I=0;Self.D=0} +function peek(){return Self.I/% \t\n"))throw new Error("Symbol expected, got "+C);var N=member(C,"+-0123456789.");var F="."==C;var L=[C];while(peek()&&!member(peek(),"()<>[]{}/% \t\n")){C=xchar();L.push(C);if(N&&!member(C,"0123456789")){if(!F&&"."==C)F=true;else N=false}} +L=L.join("");if(1==L.length&&member(L,"+-."))N=false;return N?(F?parseFloat(L):parseInt(L,10)):new Symbol(L)} +function token(){skip();switch(peek()){case false:return undefined;case"%":return comment();case"[":return new Symbol(xchar());case"]":return new Symbol(xchar());case"{":Self.D++;return new Symbol(xchar());case"}":Self.D--;return new Symbol(xchar());case"/":xchar();var X=symbol();return quote(X);case"(":return text();case"<":xchar();if("<"!=peek())throw new Error("Encoded strings not implemented yet");xchar();return new Symbol("<<");case">":xchar();if(">"!=peek())throw new Error("Unexpected >");xchar();return new Symbol(">>");default:return symbol()}} +PsParser.prototype.init=init;PsParser.prototype.peek=peek;PsParser.prototype.token=token;return this} +function Ps0(Os,Ds,Es){function run(X,Z){if(isSymbol(X)&&!isQuoted(X)){var K=symbolName(X);var D=inDs(Ds,K);if(!D) +throw new Error("bind error '"+K+"'");Es.push([false,D[K]])}else if(Z&&isArray(X)&&isQuoted(X)){if(0>"==symbolName(T))){exec();while(0>"]=function(){var D={};while(0=0) +A.push(D[K++]) +Os.push(A)};Sd["put"]=function(){var V=Os.pop();var K=Os.pop();var D=Os.pop();if(isSymbol(K))D[symbolName(K)]=V;else D[K]=V};Sd["begin"]=function(){Ds.push(Os.pop())};Sd["end"]=function(){Ds.pop()};Sd["currentdict"]=function(){Os.push(Ds[Ds.length-1])};Sd["where"]=function(){var K=symbolName(Os.pop());var D=inDs(Ds,K);if(D){Os.push(D);Os.push(true)}else Os.push(false)};Sd["save"]=function(){var X=Ds.slice();for(var I=0;I0){prop=' font-weight="bold"';j=b;flags=2} +if(i>0||o>0){if(i>0){prop+=' font-style="italic"';if(i0){prop+=' font-style="oblique"';if(o=0) +prop+=' font-weight="normal"';if(!(flags&12)&&(font_n_old.indexOf("Italic")>=0||font_n_old.indexOf("Oblique")>=0)) +prop+=' font-style="normal"'} +svgbuf+=' font-family="'+name+'"'+ +prop+' font-size="'+gcur.font_s+'"'} +function path_def(){if(path) +return +setg(1);gcur.px=gcur.cx;gcur.py=gcur.cy;path='\n";g=1} +if(newg==0){if(g){g=0;svgbuf+="\n" +if(gcur.rotate){gcur.xoffs=x_rot;gcur.yoffs=y_rot;x_rot=0;y_rot=0}}}else if(gchg){defg1()}} +function strw(s){return s.length*gcur.font_s*0.5} +Psvg.prototype.strw=strw;function arc(x,y,r,a1,a2,arcn){var x1,y1,x2,y2 +if(a1>=360) +a1-=360 +if(a2>=360) +a2-=360;x1=x+r*Math.cos(a1*Math.PI/180);y1=y+r*Math.sin(a1*Math.PI/180) +if(gcur.cx!=undefined){if(path){if(x1!=gcur.cx||y1!=gcur.cy) +path+='l' +else +path+='m';path+=(x1-gcur.cx).toFixed(1)+" "+ +(-(y1-gcur.cy)).toFixed(1)}else{gcur.cx=x1;gcur.cy=y1;path_def()}}else{if(path) +path='' +gcur.cx=x1;gcur.cy=y1;path_def()} +if(a1==a2){a2=180-a1;x2=x+r*Math.cos(a2*Math.PI/180);y2=y+r*Math.sin(a2*Math.PI/180);path+='a'+r.toFixed(2)+' '+r.toFixed(2)+' 0 0 '+ +(arcn?'1 ':'0 ')+ +(x2-x1).toFixed(2)+' '+ +(y1-y2).toFixed(2)+' '+ +r.toFixed(2)+' '+r.toFixed(2)+' 0 0 '+ +(arcn?'1 ':'0 ')+ +(x1-x2).toFixed(2)+' '+ +(y2-y1).toFixed(2)+'\n';gcur.cx=x1;gcur.cy=y1}else{x2=x+r*Math.cos(a2*Math.PI/180);y2=y+r*Math.sin(a2*Math.PI/180);path+='a'+r.toFixed(2)+' '+r.toFixed(2)+' 0 0 '+ +(arcn?'1 ':'0 ')+ +(x2-x1).toFixed(2)+' '+ +(y1-y2).toFixed(2)+'\n';gcur.cx=x2;gcur.cy=y2}} +Psvg.prototype.arc=arc +Psvg.prototype.arcn=function(x,y,r,a1,a2){arc(x,y,r,a1,a2,true)} +Psvg.prototype.closepath=function(){if(path&&gcur.cx) +rlineto(gcur.px-gcur.cx,gcur.py-gcur.cy)} +Psvg.prototype.cx=function(){return gcur.cx} +Psvg.prototype.cy=function(){return gcur.cy} +Psvg.prototype.curveto=function(x1,y1,x2,y2,x,y){path_def();path+="\tC"+ +(gcur.xoffs+x1).toFixed(1)+" "+(gcur.yoffs-y1).toFixed(1)+" "+ +(gcur.xoffs+x2).toFixed(1)+" "+(gcur.yoffs-y2).toFixed(1)+" "+ +(gcur.xoffs+x).toFixed(1)+" "+(gcur.yoffs-y).toFixed(1)+"\n";gcur.cx=x;gcur.cy=y} +Psvg.prototype.eofill=function(){path_end();svgbuf+='" fill-rule="evenodd" fill="currentColor"/>\n'} +Psvg.prototype.fill=function(){path_end();svgbuf+='" fill="currentColor"/>\n'} +Psvg.prototype.gsave=function(){gc_stack.push(objdup(gcur))} +Psvg.prototype.grestore=function(){gcur=gc_stack.pop();gchg=true} +Psvg.prototype.lineto=function(x,y){path_def() +if(x==gcur.cx) +path+="\tv"+(gcur.cy-y).toFixed(1)+"\n" +else if(y==gcur.cy) +path+="\th"+(x-gcur.cx).toFixed(1)+"\n" +else +path+="\tl"+(x-gcur.cx).toFixed(1)+" "+ +(gcur.cy-y).toFixed(1)+"\n";gcur.cx=x;gcur.cy=y} +Psvg.prototype.moveto=function(x,y){gcur.cx=x;gcur.cy=y +if(path){path+="\tM"+(gcur.xoffs+gcur.cx).toFixed(1)+" "+ +(gcur.yoffs-gcur.cy).toFixed(1)+"\n"}else if(g==2){svgbuf+="\n";g=1}} +Psvg.prototype.newpath=function(){gcur.cx=gcur.cy=undefined} +Psvg.prototype.rcurveto=function(x1,y1,x2,y2,x,y){path_def();path+="\tc"+ +x1.toFixed(1)+" "+(-y1).toFixed(1)+" "+ +x2.toFixed(1)+" "+(-y2).toFixed(1)+" "+ +x.toFixed(1)+" "+(-y).toFixed(1)+"\n";gcur.cx+=x;gcur.cy+=y} +function rlineto(x,y){path_def() +if(x==0) +path+="\tv"+(-y).toFixed(1)+"\n" +else if(y==0) +path+="\th"+x.toFixed(1)+"\n" +else +path+="\tl"+x.toFixed(1)+" "+ +(-y).toFixed(1)+"\n";gcur.cx+=x;gcur.cy+=y} +Psvg.prototype.rlineto=rlineto;Psvg.prototype.rmoveto=function(x,y){if(path){path+="\tm"+x.toFixed(1)+" "+ +(-y).toFixed(1)+"\n"}else if(g==2){svgbuf+="\n";g=1} +gcur.cx+=x;gcur.cy+=y} +Psvg.prototype.rotate=function(a){setg(0) +var x,xtmp=gcur.xoffs,y=gcur.yoffs,_sin=gcur.sin,_cos=gcur.cos;x=xtmp*_cos-y*_sin;y=xtmp*_sin+y*_cos;gcur.xoffs=x/gcur.xscale;gcur.yoffs=y/gcur.yscale;xtmp=gcur.cx;y=gcur.cy;x=xtmp*_cos-y*_sin;y=-xtmp*_sin+y*_cos;gcur.cx=x/gcur.xscale;gcur.cy=y/gcur.yscale;a=360-a;gcur.rotate+=a +if(gcur.rotate>180) +gcur.rotate-=360 +else if(gcur.rotate<=-180) +gcur.rotate+=360 +a=gcur.rotate*Math.PI/180;gcur.sin=_sin=Math.sin(a);gcur.cos=_cos=Math.cos(a);x=gcur.cx;gcur.cx=(x*_cos+gcur.cy*_sin)*gcur.xscale;gcur.cy=(-x*_sin+gcur.cy*_cos)*gcur.yscale;x=gcur.xoffs;gcur.xoffs=(x*_cos+gcur.yoffs*_sin)*gcur.xscale;gcur.yoffs=(-x*_sin+gcur.yoffs*_cos)*gcur.yscale;gchg=true} +Psvg.prototype.scale=function(sx,sy){gcur.xoffs/=sx;gcur.yoffs/=sy;gcur.cx/=sx;gcur.cy/=sy;gcur.xscale*=sx;gcur.yscale*=sy;gchg=true} +Psvg.prototype.selectfont=function(s,h){s=s.nm;if(font_s!=h||s!=font_n){gcur.font_n_old=gcur.font_n;gcur.font_n=s;gcur.font_s=h;gchg=true}} +Psvg.prototype.setdash=function(a,o){var n=a.length,i +if(n==0){gcur.dash='' +return} +gcur.dash=' stroke-dashoffset="'+o+'" stroke-dasharray="';i=0 +while(1){gcur.dash+=a[i] +if(--n==0) +break +gcur.dash+=' '} +gcur.dash+='"'} +Psvg.prototype.setlinewidth=function(w){gcur.linewidth=w} +Psvg.prototype.setrgbcolor=function(r,g,b){var rgb=0x1000000+ +(Math.floor(r*255)<<16)+ +(Math.floor(g*255)<<8)+ +Math.floor(b*255);rgb=rgb.toString(16);rgb=rgb.replace('1','#') +if(rgb!=gcur.rgb){gcur.rgb=rgb;gchg=true}} +Psvg.prototype.show=function(s){var span,x,y +if(gchg){if(g==2) +span=true +else +defg1()} +x=gcur.cx;y=gcur.cy +if(span){svgbuf+=""}else if(g!=2){if(!g) +defg1() +svgbuf+='';g=2} +svgbuf+=s.replace(/<|>|&/g,function(c){switch(c){case'<':return"<" +case'>':return">" +case'&':return"&"}}) +if(span) +svgbuf+="";gcur.cx=x+strw(s)} +Psvg.prototype.stroke=function(){path_end() +if(gcur.linewidth!=0.7) +svgbuf+='" stroke-width="'+gcur.linewidth.toFixed(1);svgbuf+='" stroke="currentColor" fill="none"'+gcur.dash+'/>\n'} +Psvg.prototype.translate=function(x,y){gcur.xoffs+=x;gcur.yoffs-=y;gcur.cx-=x;gcur.cy-=y} +Psvg.prototype.arp=function(val,x,y){var xy=getorig();ps_flush();abcobj.out_arp((x+xy[0])*abcobj.stv_g().scale,y-xy[1],val)} +Psvg.prototype.ltr=function(val,x,y){var xy=getorig();ps_flush();abcobj.out_ltr((x+xy[0])*abcobj.stv_g().scale,y-xy[1],val)} +Psvg.prototype.xygl=function(x,y,gl){var xy=getorig();ps_flush();abcobj.xygl((x+xy[0])*abcobj.stv_g().scale,y-xy[1],gl)} +Psvg.prototype.xygls=function(str,x,y,gl){var xy=getorig();ps_flush();abcobj.out_deco_str((x+xy[0])*abcobj.stv_g().scale,y-xy[1],gl,str)} +Psvg.prototype.xyglv=function(val,x,y,gl){var xy=getorig();ps_flush();abcobj.out_deco_val((x+xy[0])*abcobj.stv_g().scale,y-xy[1],gl,val)} +Psvg.prototype.y0=function(y){var staff_tb=abcobj.get_staff_tb() +return y+staff_tb[0].y} +Psvg.prototype.y1=function(y){var staff_tb=abcobj.get_staff_tb() +return y+staff_tb[1].y} +function ps_flush(g0){if(g0) +setg(0);if(!svgbuf) +return +abcobj.out_svg(svgbuf);svgbuf=''} +Psvg.prototype.ps_flush=ps_flush +Psvg.prototype.ps_eval=function(txt){wps.parse(txt);ps_flush(true)} +function pscall(f,x,y,script){gcur.xorig=gcur.xoffs=abcobj.sx(0);gcur.yorig=gcur.yoffs=abcobj.sy(0);gcur.cx=0;gcur.cy=0;wps.parse(script+ +(x/abcobj.stv_g().scale).toFixed(1)+' '+y.toFixed(1)+' '+f);ps_flush(true) +return true} +Psvg.prototype.psdeco=function(x,y,de){var dd,de2,script,defl,f=de.dd.glyph,Os=wps.parse('/'+f+' where'),A=Os.pop(),staff_tb=abcobj.get_staff_tb() +if(!A) +return false;defl=0 +if(de.defl.nost) +defl=1 +if(de.defl.noen) +defl|=2 +if(de.s.stem>=0) +defl|=4;Os.pop();script='/defl '+defl+' def ' +if(de.lden){script+=x.toFixed(1)+' '+y.toFixed(1)+' ';de2=de.start;x=de2.x;y=de2.y+staff_tb[de2.st].y +if(x>de.x-20) +x=de.x-20} +dd=de.dd +if(de.has_val){script+=de.val+' '}else if(dd.str){script+='('+dd.str+') ';y+=dd.h*0.2} +return pscall(f,x,y,script)} +Psvg.prototype.psxygl=function(x,y,gl){var Os=wps.parse('/'+gl+' where'),A=Os.pop() +if(!A) +return false +Os.pop() +return pscall(gl,x,y,'dlw ')} +Psvg.prototype.svgcall=function(f,x,y,v1,v2){var xy=getorig();ps_flush();f((x+xy[0])*abcobj.stv_g().scale,y-xy[1],v1,v2)} +wps.parse("\ +currentdict/systemdict currentdict put\n\ +systemdict/{/mark cvx put\n\ +systemdict/[/mark cvx put\n\ +systemdict/]\n\ +/counttomark cvx\n\ +/array cvx\n\ +/astore cvx\n\ +/exch cvx\n\ +/pop cvx\n\ +5 array astore cvx put\n\ +systemdict/}/] cvx/cvx cvx 2 array astore cvx put\n\ +systemdict/def{currentdict 2 index 2 index put pop pop}put\n\ +\n\ +/maxlength 1000 def % TODO\n\ +/.bdef{bind def}bind def\n\ +/.xdef{exch def}.bdef\n\ +/dup{0 index}.bdef\n\ +/load{dup where pop exch get}.bdef\n\ +/.ldef{load def}.bdef\n\ +/if{{}ifelse}.bdef\n\ +/cleartomark{array pop}.bdef\n\ +/known{exch begin where{currentdict eq}{false}if end}.bdef\n\ +/store{1 index where{3 1 roll put}{def}ifelse}.bdef\n\ +/not{{false}{true}ifelse}.bdef\n\ +%/.logand{{{true}{false}ifelse}{pop false}ifelse}.bdef\n\ +%/and/.logand .ldef % TODO numeric and\n\ +/.logor{{pop true}{{true}{false}ifelse}ifelse}.bdef\n\ +/or/.logor .ldef % TODO numeric or\n\ +/ne{eq not}.bdef\n\ +/ge{lt not}.bdef\n\ +/le{1 index 1 index eq 3 1 roll lt or}.bdef\n\ +/gt{le not}.bdef\n\ +/.repeat{1 1 4 2 roll for}.bdef\n\ +\n\ +%% math\n\ +\n\ +/floor{.math(floor)1 .call}.bdef\n\ +\n\ +/neg{0 exch sub}.bdef\n\ +/add{neg sub}.bdef\n\ +/idiv{div floor}.bdef\n\ +\n\ +/.pi{.math(PI)get}.bdef\n\ +\n\ +/abs{.math(abs)1 .call}.bdef\n\ +%/.acos{.math(acos)1 .call}.bdef\n\ +%/.asin{.math(asin)1 .call}.bdef\n\ +/atan{.math(atan2)2 .call 180 mul .pi div}.bdef\n\ +%/.atan2{.math(atan2)2 .call}.bdef\n\ +%/ceiling{.math(ceil)1 .call}.bdef\n\ +/cos{.pi mul 180 div .math(cos)1 .call}.bdef\n\ +%/.exp{.math(exp)1 .call}.bdef\n\ +%/log{.math(log)1 .call}.bdef\n\ +%/.max{.math(max)2 .call}.bdef\n\ +%/.min{.math(min)2 .call}.bdef\n\ +%/.pow{.math(pow)2 .call}.bdef\n\ +%/.random{.math(random)0 .call}.bdef\n\ +%/rand{.random}.bdef % TODO follow spec\n\ +%/round{.math(round)1 .call}.bdef\n\ +%/sin{.math(sin)1 .call}.bdef\n\ +%/sqrt{.math(sqrt)1 .call}.bdef\n\ +%/.tan{.math(tan)1 .call}.bdef\n\ +%/truncate{.math(truncate)1 .call}.bdef % TODO Math.truncate does not exist!\n\ +\n\ +% graphic\n\ +/arc{.svg(arc)5 .call0}.bdef\n\ +/arcn{.svg(arcn)5 .call0}.bdef\n\ +/closepath{.svg(closepath)0 .call}.bdef\n\ +/currentpoint{.svg(cx)0 .call .svg(cy)0 .call}.bdef\n\ +/curveto{.svg(curveto)6 .call0}.bdef\n\ +/eofill{.svg(eofill)0 .call0}.bdef\n\ +/fill{.svg(fill)0 .call0}.bdef\n\ +/grestore{.svg(grestore)0 .call0}.bdef\n\ +/gsave{.svg(gsave)0 .call0}.bdef\n\ +/lineto{.svg(lineto)2 .call0}.bdef\n\ +/moveto{.svg(moveto)2 .call0}.bdef\n\ +/newpath{.svg(newpath)0 .call0}.bdef\n\ +/rcurveto{.svg(rcurveto)6 .call0}.bdef\n\ +/rlineto{.svg(rlineto)2 .call0}.bdef\n\ +/rmoveto{.svg(rmoveto)2 .call0}.bdef\n\ +/rotate{.svg(rotate)1 .call0}.bdef\n\ +/scale{.svg(scale)2 .call0}.bdef\n\ +/selectfont{.svg(selectfont)2 .call0}.bdef\n\ +/setdash{.svg(setdash)2 .call0}.bdef\n\ +/setlinewidth{.svg(setlinewidth)1 .call0}.bdef\n\ +/setrgbcolor{.svg(setrgbcolor)3 .call0}.bdef\n\ +/show{.svg(show)1 .call0}.bdef\n\ +/stroke{.svg(stroke)0 .call0}.bdef\n\ +/stringwidth{.svg(strw)1 .call 1}.bdef %fixme: height KO\n\ +/translate{.svg(translate)2 .call0}.bdef\n\ +\n\ +/setgray{255 mul dup dup setrgbcolor}.bdef\n\ +% abcm2ps syms.c\n\ +/!{bind def}bind def\n\ +/T/translate load def\n\ +/M/moveto load def\n\ +/RM/rmoveto load def\n\ +/L/lineto load def\n\ +/RL/rlineto load def\n\ +/C/curveto load def\n\ +/RC/rcurveto load def\n\ +/SLW/setlinewidth load def\n\ +/defl 0 def\n\ +/dlw{0.7 SLW}!\n\ +/xymove{/x 2 index def/y 1 index def M}!\n\ +/showc{dup stringwidth pop .5 mul neg 0 RM show}!\n\ +%\n\ +% abcm2ps internal glyphs\n\ +/arp{.svg(arp)3 .call0}.bdef\n\ +/ltr{.svg(ltr)3 .call0}.bdef\n\ +/ft0{(acc-1).svg(xygl)3 .call0}.bdef\n\ +/nt0{(acc3).svg(xygl)3 .call0}.bdef\n\ +/sh0{(acc1).svg(xygl)3 .call0}.bdef\n\ +/dsh0{(acc2).svg(xygl)3 .call0}.bdef\n\ +/trl{(trl).svg(xygl)3 .call0}.bdef\n\ +/lmrd{(lmrd).svg(xygl)3 .call0}.bdef\n\ +/turn{(turn).svg(xygl)3 .call0}.bdef\n\ +/umrd{(umrd).svg(xygl)3 .call0}.bdef\n\ +/y0{.svg(y0)1 .call}.bdef\n\ +/y1{.svg(y1)1 .call}.bdef\n")} +abc2svg.psvg={do_begin_end:function(of,type,opt,text){if(type!="ps"){of(type,opt,text) +return} +if(opt=='nosvg') +return +if(!this.psvg) +this.psvg=new Psvg(this);this.psvg.ps_eval.call(this.psvg,text)},psdeco:function(of,x,y,de){if(!this.psvg) +return false +return this.psvg.psdeco.call(this.psvg,x,y,de)},psxygl:function(of,x,y,gl){if(!this.psvg) +return false +return this.psvg.psxygl.call(this.psvg,x,y,gl)},set_hooks:function(abc){abc.do_begin_end=abc2svg.psvg.do_begin_end.bind(abc,abc.do_begin_end);abc.psdeco=abc2svg.psvg.psdeco.bind(abc,abc.psdeco);abc.psxygl=abc2svg.psvg.psxygl.bind(abc,abc.psxygl)}} +if(!abc2svg.mhooks) +abc2svg.mhooks={} +abc2svg.mhooks.psvg=abc2svg.psvg.set_hooks diff --git a/lib/ChordPro/res/abc/abc2svg/roman-1.js b/lib/ChordPro/res/abc/abc2svg/roman-1.js new file mode 100644 index 00000000..519cb7de --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/roman-1.js @@ -0,0 +1,55 @@ +//abc2svg-roman.js-convert the chord symbols to the RNN +if(typeof abc2svg=="undefined") +var abc2svg={} +abc2svg.roman={note_nm:"CDEFGAB",nm_M:["I","♯I","II","♭III","III","IV","♯IV","V","♯V","VI","♭VII","VII"],nm_m:["I","♭II","II","III","♯III","IV","♭V","V","VI","♯VI","VII","♯VII"],gch_build:function(of,s){var gch,ix,t,ty=this.cfmt().roman +function set_nm(p){var i,o,o2,a,n,csa=[] +i=p.indexOf('/') +while(i>0){if(p[i-1]!='<') +break +i=p.indexOf('/',i+1)} +if(i<0){csa.push(p)}else{csa.push(p.slice(0,i)) +csa.push(p.slice(i+1))} +for(i=0;i=out.length) +o=-1} +if(!res||o<0){if(conf.onend) +conf.onend() +return}} +current=out[o]=='sf2'?audio5:midi5;abcplay.play=current.play;abcplay.stop=current.stop +if(current.set_output) +current.set_output(out[o]);abcplay.play(init.istart,init.i_iend,init.a_e)} +conf.gain=0.7;conf.speed=1;(function(){var v +try{if(!localStorage) +return}catch(e){return} +if(!conf.sfu){v=localStorage.getItem("sfu") +if(v) +conf.sfu=v} +v=localStorage.getItem("volume") +if(v) +conf.gain=Number(v)})() +if(typeof Midi5=="function") +midi5=Midi5(conf) +if(typeof Audio5=="function") +audio5=Audio5(conf);return abcplay} +if(typeof module=='object'&&typeof exports=='object') +exports.AbcPlay=AbcPlay +if(!abc2svg) +var abc2svg={} +function ToAudio(){return{add:function(first,voice_tb,cfmt){var toaud=this,C=abc2svg.C,p_time=0,abc_time=0,play_fac=C.BLEN/4*120/60,i,n,dt,d,v,s=first,rst=s,rst_fac,rsk=[],b_tim,b_typ +function get_beat(){var s=first.p_v.meter +if(!s.a_meter[0]) +return C.BLEN/4 +if(!s.a_meter[0].bot) +return(s.a_meter[1]&&s.a_meter[1].top=='|')?C.BLEN/2:C.BLEN/4 +if(s.a_meter[0].bot=="8"&&s.a_meter[0].top%3==0) +return C.BLEN/8*3 +return C.BLEN/s.a_meter[0].bot|0} +function def_beats(){var i,s2,s3,tim,beat=get_beat(),d=first.p_v.meter.wmeasure,nb=d/beat|0,v=voice_tb.length,p_v={id:"_beats",v:v,sym:{type:C.BLOCK,v:v,subtype:"midiprog",chn:9,instr:16384,ts_prev:first}},s={type:C.NOTE,v:v,p_v:p_v,dur:beat,nhd:0,notes:[{midi:37}]} +abc_time=-d +for(s2=first;s2;s2=s2.ts_next){if(s2.bar_type&&s2.time){nb=(2*d-s2.time)/beat|0 +abc_time-=d-s2.time +break}} +s2=p_v.sym +for(s3=first;s3&&!s3.time;s3=s3.ts_next){if(s3.type==C.TEMPO){s3=Object.create(s3) +s3.v=v +s3.p_v=p_v +s3.prev=s3.ts_prev=s2 +s2.next=s2.ts_next=s3 +s2=s3 +play_fac=set_tempo(s2) +break}} +voice_tb[v]=p_v +p_v.sym.p_v=p_v +first.time=s2.time=tim=abc_time +if(s3) +p_v.sym.time=tim +for(i=0;i='A'&&c<='Z'){j=r.length +r+=c +continue} +n=Number(c) +if(isNaN(n)) +break +v=r.slice(j) +if(r.length+v.length*n>128) +continue +while(--n>0) +r+=v} +s.parts=r +s.p_s=[] +while(1){if(!s.ts_next){s.part1=first +break} +s=s.ts_next +if(s.part){s.part1=first +v=s.part.text[0] +for(i=0;i='1'&&d<='9') +rsk[Number(d)]=s +else if(d!=',') +rsk.push(s)}} +if(cfmt.chord) +abc2svg.chord(first,voice_tb,cfmt) +if(cfmt.playbeats) +def_beats() +if(s.parts) +build_parts(s) +rst_fac=play_fac +while(s){if(s.noplay){s=s.ts_next +continue} +dt=s.time-abc_time +if(dt!=0){p_time+=dt/play_fac +abc_time=s.time} +s.ptim=p_time +if(s.part){rst=s +rst_fac=play_fac} +switch(s.type){case C.BAR:if(s.time!=b_tim){b_tim=s.time +b_typ=0} +if(s.text&&rsk.length>1&&s.text[0]!='1'){if(b_typ&1) +break +b_typ|=1 +set_variant(s) +play_fac=rst_fac +rst=rsk[0]} +if(s.bar_type[0]==':'){if(b_typ&2) +break +b_typ|=2 +s.rep_p=rst +if(rst==rsk[0]) +s.rep_v=rsk} +if(s.text){if(s.text[0]=='1'){if(b_typ&1) +break +b_typ|=1 +s.rep_s=rsk=[rst] +if(rst.bar_type&&rst.bar_type.slice(-1)!=':') +rst.bar_type+=':' +set_variant(s) +rst_fac=play_fac}}else if(s.bar_type.slice(-1)==':'){if(b_typ&4) +break +b_typ|=4 +rst=s +rst_fac=play_fac} +break +case C.GRACE:if(s.time==0&&abc_time==0){dt=0 +if(s.sappo) +dt=C.BLEN/16 +else if(!s.next||s.next.type!=C.NOTE) +dt=d/2 +abc_time-=dt} +gen_grace(s) +break +case C.REST:case C.NOTE:d=s.dur +if(s.next&&s.next.type==C.GRACE){dt=0 +if(s.next.sappo) +dt=C.BLEN/16 +else if(!s.next.next||s.next.next.type!=C.NOTE) +dt=d/2 +s.next.time-=dt +d-=dt} +d/=play_fac +s.pdur=d +v=s.v +break +case C.TEMPO:if(s.tempo) +play_fac=set_tempo(s) +break} +s=s.ts_next}}}} +abc2svg.play_next=function(po){function do_tie(not_s,d){var i,s=not_s.s,C=abc2svg.C,v=s.v,end_time=s.time+s.dur,repv=po.repv +while(1){s=s.ts_next +if(!s||s.time>end_time) +break +if(s.type==C.BAR){if(s.rep_p){if(!po.repn){s=s.rep_p +end_time=s.time}} +if(s.rep_s){if(!s.rep_s[repv]) +break +s=s.rep_s[repv++] +end_time=s.time} +while(s.ts_next&&!s.ts_next.dur) +s=s.ts_next +continue} +if(s.time=0){note=s.notes[i] +if(note.tie_s==not_s){d+=s.pdur/po.conf.speed +return note.tie_e?do_tie(note,d):d}}} +return d} +function set_ctrl(po,s2,t){var i,p_v=s2.p_v,s={subtype:"midictl",p_v:p_v,v:s2.v} +for(i in p_v.midictl){s.ctrl=Number(i) +s.val=p_v.midictl[i] +po.midi_ctrl(po,s,t)} +for(s=p_v.sym;s!=s2;s=s.next){if(s.subtype=="midictl") +po.midi_ctrl(po,s,t) +else if(s.subtype=='midiprog') +po.midi_prog(po,s)} +i=po.v_c[s2.v] +if(i==undefined) +po.v_c[s2.v]=i=s2.v<9?s2.v:s2.v+1 +if(po.c_i[i]==undefined) +po.c_i[i]=0 +po.p_v[s2.v]=true} +function play_cont(po){var d,i,st,m,note,g,s2,t,maxt,now,C=abc2svg.C,s=po.s_cur +function var_end(s){var i,s2,s3,a=s.rep_v||s.rep_s +ti=0 +for(i=1;iti){ti=s2.time +s3=s2}} +for(s=s3;s!=po.s_end;s=s.ts_next){if(s.time==ti) +continue +if(s.rbstop==2) +break} +po.repv=1 +return s} +if(po.stop){if(po.onend) +po.onend(po.repv) +return} +while(s.noplay){s=s.ts_next +if(!s||s==po.s_end){if(po.onend) +po.onend(po.repv) +return}} +t=po.stim+s.ptim/po.conf.speed +now=po.get_time(po) +if(po.conf.new_speed){po.stim=now-(now-po.stim)*po.conf.speed/po.conf.new_speed +po.conf.speed=po.conf.new_speed +po.conf.new_speed=0 +t=po.stim+s.ptim/po.conf.speed} +maxt=t+po.tgen +po.timouts=[] +while(1){if(!po.p_v[s.v]) +set_ctrl(po,s,t) +switch(s.type){case C.BAR:s2=null +if(s.rep_p){po.repv++ +if(!po.repn&&(!s.rep_v||po.repv<=s.rep_v.length)){s2=s.rep_p +po.repn=true}else{if(s.rep_v) +s2=var_end(s) +po.repn=false}} +if(s.rep_s){s2=s.rep_s[po.repv] +if(s2){po.repn=false +if(s2==s) +s2=null}else{s2=var_end(s) +if(s2==po.s_end) +break}} +if(s.bar_type.slice(-1)==':'&&s.bar_type[0]!=':') +po.repv=1 +if(s2){po.stim+=(s.ptim-s2.ptim)/po.conf.speed +s=s2 +while(s&&!s.dur) +s=s.ts_next +if(!s) +break +t=po.stim+s.ptim/po.conf.speed +break} +if(!s.part1){while(s.ts_next&&!s.ts_next.seqst){s=s.ts_next +if(s.part1) +break} +if(!s.part1) +break} +default:if(s.part1&&po.i_p!=undefined){s2=s.part1.p_s[++po.i_p] +if(s2){po.stim+=(s.ptim-s2.ptim)/po.conf.speed +s=s2 +t=po.stim+s.ptim/po.conf.speed}else{s=po.s_end} +po.repv=1} +break} +if(s&&s!=po.s_end){switch(s.type){case C.BAR:break +case C.BLOCK:if(s.subtype=="midictl") +po.midi_ctrl(po,s,t) +else if(s.subtype=='midiprog') +po.midi_prog(po,s) +break +case C.GRACE:for(g=s.extra;g;g=g.next){d=g.pdur/po.conf.speed +for(m=0;m<=g.nhd;m++){note=g.notes[m] +if(!note.noplay) +po.note_run(po,g,note.midi,t+g.ptim-s.ptim,d)}} +break +case C.NOTE:case C.REST:d=s.pdur/po.conf.speed +if(s.type==C.NOTE){for(m=0;m<=s.nhd;m++){note=s.notes[m] +if(note.tie_s||note.noplay) +continue +po.note_run(po,s,note.midi,t,note.tie_e?do_tie(note,d):d)}} +if(po.onnote&&s.istart){i=s.istart +st=(t-now)*1000 +po.timouts.push(setTimeout(po.onnote,st,i,true)) +if(d>2) +d-=.1 +setTimeout(po.onnote,st+d*1000,i,false)} +break}} +while(1){if(!s||s==po.s_end||!s.ts_next){if(po.onend) +setTimeout(po.onend,(t-now+d)*1000,po.repv) +po.s_cur=s +return} +s=s.ts_next +if(!s.noplay) +break} +t=po.stim+s.ptim/po.conf.speed +if(t>maxt) +break} +po.s_cur=s +po.timouts.push(setTimeout(play_cont,(t-now)*1000 +-300,po))} +function get_part(po){var s,i,s_p +for(s=po.s_cur;s;s=s.ts_prev){if(s.parts){po.i_p=-1 +return} +s_p=s.part1 +if(!s_p||!s_p.p_s) +continue +for(i=0;i>16)&0xff +a[j++]=(t>>8)&0xff +a[j++]=t&0xff} +if(l!=s.length){t=(b64d[s[i]]<<18)+ +(b64d[s[i+1]]<<12)+ +(b64d[s[i+2]]<<6)+ +b64d[s[i+3]] +a[j++]=(t>>16)&0xff +if(j>8)&0xff} +return a} +function sample_cp(b,s){var i,n,a=b.getChannelData(0) +for(i=0;i>7,p=instr%128,pr=sf2pre +rates[instr]=[] +for(i=0;i=.4) +parm.sustain=0.01 +else +parm.sustain=1-parm.sustain/.4 +sample_cp(parm.buffer,sample) +if(parm.sm){parm.loopStart=sf2par.sampleHeader[sid].startLoop/sampleRate +parm.loopEnd=sf2par.sampleHeader[sid].endLoop/sampleRate} +scale=(gen.scaleTuning?gen.scaleTuning.amount:100)/100,tune=(gen.coarseTune?gen.coarseTune.amount:0)+ +(gen.fineTune?gen.fineTune.amount:0)/100+ +sf2par.sampleHeader[sid].pitchCorrection/100- +(gen.overridingRootKey?gen.overridingRootKey.amount:sf2par.sampleHeader[sid].originalPitch) +for(j=gen.keyRange.lo;j<=gen.keyRange.hi;j++){rates[instr][j]=Math.pow(Math.pow(2,1/12),(j+tune)*scale) +params[instr][j]=parm}}}} +function load_instr(instr){w_instr++ +abc2svg.loadjs(conf.sfu+'/'+instr+'.js',function(){var sf2par=new sf2.Parser(b64dcod(abcsf2[instr])) +sf2par.parse() +var sf2pre=sf2par.getPresets() +sf2_create(instr,sf2par,sf2pre) +if(--w_instr==0) +play_start()},function(){errmsg('could not find the instrument '+ +((instr/128)|0).toString()+'-'+ +(instr%128).toString()) +if(--w_instr==0) +play_start()})} +function def_instr(s,f,sf2par,sf2pre){var i,bk=[],nv=-1,vb=0 +s=s.p_v.sym +while(s.ts_prev) +s=s.ts_prev +for(;s;s=s.ts_next){if(s.v>nv){nv=s.v +bk[nv]=0 +if(s.p_v.midictl){if(s.p_v.midictl[0]) +bk[s.v]=(bk[s.v]&~0x1fc000) ++(s.p_v.midictl[0]<<14) +if(s.p_v.midictl[32]) +bk[s.v]=(bk[s.v]&~0x3f80) ++(s.p_v.midictl[32]<<7)}} +switch(s.subtype){case"midiprog":break +case"midictl":if(s.ctrl!=0&&s.ctrl!=32) +continue +if(bk[s.v]==undefined) +bk[s.v]=0 +if(s.ctrl==0) +bk[s.v]=(bk[s.v]&~0x1fc000) ++(s.val<<14) +else +bk[s.v]=(bk[s.v]&~0x3f80) ++(s.val<<7) +default:continue} +vb|=1<>>0,genre:(data[ip++]|(data[ip++]<<8)|(data[ip++]<<16)|(data[ip++]<<24))>>>0,morphology:(data[ip++]|(data[ip++]<<8)|(data[ip++]<<16)|(data[ip++]<<24))>>>0})}};sf2.Parser.prototype.parsePbag=function(chunk){var data=this.input,ip=chunk.offset,presetZone=this.presetZone=[],size=chunk.offset+chunk.size;if(chunk.type!=='pbag') +throw new Error('invalid chunk type:'+chunk.type);while(ip>24;sampleLink=data[ip++]|(data[ip++]<<8);sampleType=data[ip++]|(data[ip++]<<8);var sample=new Int16Array(new Uint8Array(data.subarray(this.samplingData.offset+start*2,this.samplingData.offset+end*2)).buffer);startLoop-=start;endLoop-=start;if(sampleRate>0){var adjust=this.adjustSampleData(sample,sampleRate);sample=adjust.sample;sampleRate*=adjust.multiply;startLoop*=adjust.multiply;endLoop*=adjust.multiply} +samples.push(sample);sampleHeader.push({sampleName:sampleName,startLoop:startLoop,endLoop:endLoop,sampleRate:sampleRate,originalPitch:originalPitch,pitchCorrection:pitchCorrection,sampleLink:sampleLink,sampleType:sampleType})}};sf2.Parser.prototype.adjustSampleData=function(sample,sampleRate){var newSample,i,il,j,multiply=1;while(sampleRate<22050){newSample=new Int16Array(sample.length*2);for(i=j=0,il=sample.length;i>16,lo:data[ip++],hi:data[ip++]}})}else{switch(key){case'keyRange':case'velRange':case'keynum':case'velocity':output.push({type:key,value:{lo:data[ip++],hi:data[ip++]}});break;default:output.push({type:key,value:{amount:data[ip++]|(data[ip++]<<8)<<16>>16}});break}} +ip+=2;ip+=2} +return output};sf2.Parser.prototype.parseGenerator=function(chunk){var data=this.input,ip=chunk.offset,size=chunk.offset+chunk.size,code,key,output=[];while(ip>16,lo:data[ip++],hi:data[ip++]}});continue} +switch(key){case'keynum':case'keyRange':case'velRange':case'velocity':output.push({type:key,value:{lo:data[ip++],hi:data[ip++]}});break;default:output.push({type:key,value:{amount:data[ip++]|(data[ip++]<<8)<<16>>16}});break}} +return output};sf2.Parser.prototype.getPresets=function(){var preset=this.presetHeader,zone=this.presetZone,output=[],bagIndex,bagIndexEnd,zoneInfo,presetGenerator,presetModulator,i,il,j,jl +for(i=0,il=preset.length;i0){os=access.outputs.values() +while(1){o=os.next() +if(!o||o.done) +break +out.push(o.value.name)}} +rf(out)} +return{get_outputs:function(f){if(!navigator.requestMIDIAccess){f() +return} +rf=f +navigator.requestMIDIAccess({sysex:true}).then(send_outputs,function(msg){navigator.requestMIDIAccess().then(send_outputs,function(msg){rf()})})},set_output:function(name){if(!Midi5.ma) +return +var o,os=Midi5.ma.outputs.values() +while(1){o=os.next() +if(!o||o.done) +break +if(o.value.name==name){op=o.value +break}}},play:function(i_start,i_end,i_lvl){po={conf:conf,onend:conf.onend||empty,onnote:conf.onnote||empty,s_end:i_end,s_cur:i_start,repv:i_lvl||0,tgen:2,get_time:get_time,midi_ctrl:midi_ctrl,midi_prog:midi_prog,note_run:note_run,timouts:[],op:op,v_c:[],c_i:[]} +if(0){op.send(new Uint8Array([0xf0,0x7f,0x7f,0x08,0x02,0x00,0x01,0x69,0x69,0x00,0,0xf7]),t)} +abc2svg.play_next(po)},stop:function(){po.stop=true +po.timouts.forEach(function(id){clearTimeout(id)}) +abc2svg.play_next(po) +if(op&&op.clear) +op.clear()}}} +function follow(abc,user,playconf){var keep_types={note:true,rest:true} +user.anno_stop=function(type,start,stop,x,y,w,h){if(!keep_types[type]) +return +abc.out_svg('\n')} +playconf.onnote=function(i,on){var b,i,e,elts,x=0,y=0 +if(abc2svg.mu) +elts=abc2svg.mu.d.getElementsByClassName('_'+i+'_') +else +elts=document.getElementsByClassName('_'+i+'_') +if(!elts||!elts.length) +return +e=elts[0] +e.style.fillOpacity=on?0.4:0 +if(on&&!window.no_scroll){b=e.getBoundingClientRect() +if(b.top<0||b.bottom>window.innerHeight*.8) +y=b.top-window.innerHeight*.3 +if(b.left<0||b.right>window.innerWidth*.8) +x=b.left-window.innerWidth*.3 +if(x||y) +window.scrollBy({top:y,left:x,behavior:(x<0||y)?'instant':'smooth'})}}} +(function(){var sty=document.createElement("style") +sty.innerHTML=".abcr {fill: #d00000; fill-opacity: 0; z-index: 15}" +document.head.appendChild(sty)})() +abc2svg.ch_names={'':["C-E G C+","E-C G C+","G-C E G "],m:["C-e G C+","e-C G C+","G-C e G "],'7':["C-b-E G ","E-C G b ","G-E b C+","b-E G C+"],m7:["C-b-e G ","e-C G b ","G-e b C+","b-e G C+"],m7b5:["C-b-e g ","e-C g b ","g-e b C+","b-e g C+"],M7:["C-B-E G ","E-C G B ","G-E B C+","B-E G C+"],'6':["C-A-E G ","E-C A B ","A-E B C+","B-E A C+"],m6:["C-A-e G ","e-C A B ","A-e B C+","B-e A C+"],aug:["C-E a C+","E-C a C+","a-C E a "],aug7:["C-b-E a ","E-C a b ","a-E b C+","b-E a C+"],dim:["C-e g C+","e-C g C+","g-C e g "],dim7:["C-e g A ","e-C g A ","g-e A C+","A-C e G "],'9':["C-b-E G D+","E-C G b D+","G-E b C+D+","b-E G C+D+","D-G-C E b "],m9:["C-b-e G D+","e-C G b D+","G-e b C+D+","b-e G C+D+","D-G-C e b "],maj9:["C-B-E G D+","E-C G B D+","G-E B C+D+","B-E G C+D+","D-G-C E B "],M9:["C-B-E G D+","E-C G B D+","G-C E B D+","B-E G C+D+","D-G-C E B "],'11':["C-b-E G D+F+","E-C G b D+F+","G-E b C+D+F+","b-E G C+D+F+","D-G-C E b F+","F-D-G-C E b D+"],dim9:["C-A-e g d+","e-C g A d+","g-C e A d+","A-C e g d+","D-g-C e A "],sus4:["C-F G C+","F-C G C+","G-C F G "],sus9:["C-D G C+","D-C G C+","G-C D G "],'7sus4':["C-b-F G ","F-C G b ","G-F b C+","b-C F G "],'7sus9':["C-b-D G ","D-C G b ","G-D b C+","b-C D G "],'5':["C-G C+","G-G C+"]} +abc2svg.midlet="CdDeEFgGaAbB" +abc2svg.letmid={C:0,d:1,D:2,e:3,E:4,F:5,g:6,G:7,a:8,A:9,b:10,B:11} +abc2svg.chord=function(first,voice_tb,cfmt){var chnm,i,k,vch,s,gchon,C=abc2svg.C,trans=48+(cfmt.chord.trans?cfmt.chord.trans*12:0) +function chcr(b,ch){var i,v,r=[] +b=abc2svg.midlet[b] +i=ch.length +while(--i>0){if(ch[i][0]==b) +break} +ch=ch[i] +for(i=0;i=12)b=0;break +case"b":case"♭":b--;if(b<0)b=11;break}}}} +if(b==undefined) +b=0 +ch=chcr(b,ch) +n=ch.length +r+=trans +if(sb.p_v.tr_snd) +r+=sb.p_v.tr_snd +for(m=0;m=12?"+":" ")} +chnm[k]=[vch]}}else{chnm=abc2svg.ch_names} +k=0 +for(i=0;i0) +s.ymx=s.ys+2.5 +else +s.ymn=s.ys-2.5;s=s.next +if(s==s2) +break}},set_sth:function(){var s,h,v,sth_a,p_voice,voice_tb=this.get_voice_tb() +for(v=0;v=0){s.ys=s.y+h;s.ymx=(s.ys+2.5)|0}else{s.ys=s.y-h;s.ymn=(s.ys-2.5)|0} +s.sth=s.ys}}},calculate_beam:function(of,bm,s1){var done=of(bm,s1) +if(done&&bm.s2&&(s1.sth||bm.s2.sth)) +abc2svg.sth.recal_beam.call(this,bm,s1) +return done},new_note:function(of,grace,tp_fact){var C=abc2svg.C,s=of(grace,tp_fact),curvoice=this.get_curvoice() +if(curvoice.sth&&s&&s.type==C.NOTE){s.sth=curvoice.sth;curvoice.sth=null} +return s},set_fmt:function(of,cmd,param){if(cmd=="sth"){var curvoice=this.get_curvoice() +if(curvoice) +curvoice.sth=param.split(/[ \t;-]+/) +return} +of(cmd,param)},set_stems:function(of){of();abc2svg.sth.set_sth.call(this)},set_hooks:function(abc){abc.calculate_beam=abc2svg.sth.calculate_beam.bind(abc,abc.calculate_beam);abc.new_note=abc2svg.sth.new_note.bind(abc,abc.new_note);abc.set_format=abc2svg.sth.set_fmt.bind(abc,abc.set_format);abc.set_stems=abc2svg.sth.set_stems.bind(abc,abc.set_stems)}} +if(!abc2svg.mhooks) +abc2svg.mhooks={} +abc2svg.mhooks.sth=abc2svg.sth.set_hooks diff --git a/lib/ChordPro/res/abc/abc2svg/strtab-1.js b/lib/ChordPro/res/abc/abc2svg/strtab-1.js new file mode 100644 index 00000000..abf13c66 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/strtab-1.js @@ -0,0 +1,330 @@ +//abc2svg-strtab.js-tablature for string instruments +if(typeof abc2svg=="undefined") +var abc2svg={} +abc2svg.strtab={draw_symbols:function(of,p_v){var s,m,not,stb,x,y,g,C=abc2svg.C,abc=this +function draw_heads(stb,s){var m,not,x,y +for(m=0;m<=s.nhd;m++){not=s.notes[m] +if(not.nb<0) +continue +x=s.x-3 +if(not.nb>=10) +x-=3 +y=3*(not.pit-18) +abc.out_svg(''+not.nb+'\n')}} +function draw_stems(stb,s){if(!s.tabst) +return +var s1,s2,nfl,l,y=stb+3*(s.notes[0].pit-19)*s.p_v.staffscale,h=(11+3*(s.notes[0].pit-18))*s.p_v.staffscale +abc.out_svg('\n') +if(s.nflags<=0||!s.beam_end) +return +y-=h +if(s.beam_st){abc.out_svg('' ++String.fromCharCode(0xe23f+2*s.nflags) ++'\n') +return} +s2=s +nfl=s.nflags +while(1){if(s.nflags>nfl) +nfl=s.nflags +if(s.beam_st) +break +s=s.prev} +s1=s +l=(s2.x-s1.x).toFixed(1) +abc.out_svg('\n') +if(nfl==1) +return +y+=5 +while(1){while(s.nflags<2){s=s.next +if(s==s2) +break} +if(s==s2) +break +s1=s +while(s.next.nflags>=2){s=s.next +if(s==s2) +break} +l=(s.x-s1.x).toFixed(1) +abc.out_svg('\n') +if(s==s2) +break +s=s.next +if(s==s2) +break}} +if(!p_v.tab){of(p_v) +return} +m=abc.cfmt().bgcolor||"white" +if(abc.bgt!=m){if(!abc.bgn) +abc.bgn=1 +else +abc.bgn++ +abc.bgt=m +abc.defs_add('\ +\n\ +\n\ +\n\ +') +abc.add_style('\n.bg'+abc.bgn+'{filter:url(#bg'+abc.bgn+')}')} +for(s=p_v.sym;s;s=s.next){switch(s.type){case C.KEY:case C.METER:case C.REST:s.invis=true +break}} +of(p_v) +abc.glout() +stb=abc.get_staff_tb()[p_v.st].y +abc.set_sscale(-1) +for(s=p_v.sym;s;s=s.next){switch(s.type){case C.GRACE:for(g=s.extra;g;g=g.next) +draw_stems(stb,g) +break +case C.NOTE:draw_stems(stb,s) +break}} +abc.set_scale(p_v.sym) +abc.out_svg('\n') +for(s=p_v.sym;s;s=s.next){switch(s.type){case C.GRACE:for(g=s.extra;g;g=g.next) +draw_heads(stb,g) +break +case C.NOTE:draw_heads(stb,s) +break}} +abc.out_svg('\n')},csan_bld:function(of,s){if(s.p_v.tab){var i,gch,fmt=this.cfmt() +for(i=0;i=0){this.set_v_param("diafret",true) +parm=parm.replace(/\s*diafret\s*/,"")} +this.set_v_param("strings",parm) +return +case"minfret":this.set_v_param("minfret",parm) +return} +of(cmd,parm)},set_stems:function(of){var p_v,i,m,nt,n,bi,bn,strss,g,C=abc2svg.C,abc=this,s=abc.get_tsfirst(),strs=[],lstr=[] +function set_pit(p_v,s,nt,i){var st=s.st +if(i>=0){nt.nb=(p_v.diafret?nt.pit:nt.midi)-p_v.tab[i] +if(p_v.diafret&&nt.acc) +n+='+' +nt.pit=i*2+18}else{nt.nb=-1 +nt.pit=18} +nt.acc=0 +nt.invis=true +if(!s.grace) +strss[i]=s.time+s.dur +if(s.dur<=C.BLEN/2&&!s.stemless){if(!lstr[st]) +lstr[st]=[10] +if(lstr[st][0]>i){lstr[st][0]=i +lstr[st][1]=s} +s.stemless=true}} +function set_notes(p_v,s){var i,bi,bn,nt,m,n,ns +s.stem=-1 +if(!s.nhd&&s.a_dd){i=s.a_dd.length +while(--i>=0){bi=strnum(s.a_dd[i].name) +if(bi>=0){nt=s.notes[0] +set_pit(p_v,s,nt,bi) +break}}} +delete s.a_dd +if(s.sls){for(i=0;i=0){bi=strnum(nt.a_dd[i].name) +if(bi>=0){set_pit(p_v,s,nt,bi) +delete nt.a_dd +continue ls}} +delete nt.a_dd} +bn=100 +bi=-1 +ns=i=p_v.tab.length +while(--i>=0){if(strss[i]&&strss[i]>s.time) +continue +n=(p_v.diafret?nt.pit:nt.midi)- +p_v.tab[i] +if(n>=0&&n=p_v.minfret[ns-i])){bi=i +bn=n}} +set_pit(p_v,s,nt,bi)} +s.y=3*(nt.pit-18) +s.ymn=0} +function strnum(n){n=n.match(/^([1-9])s?$/) +return n?p_v.tab.length-n[1]:-1} +p_v=abc.get_voice_tb() +for(n=0;n=0){p_v.diafret=true +strs=strs.replace(/\s*diafret\s*/,"")}} +if(strs){e=strs.slice(-1) +if(e>='1'&&e<='9') +tab=str2tab(strs.split(',')) +else +tab=abc2tab(strs) +if(!tab){this.syntax(1,"Bad strings in tablature") +ok=false}}else if(!p_v.tab){tab=p_v.diafret?[10,14,17]:[40,45,50,55,59,64]}else{tab=p_v.tab}} +if(ok){if(p_v.capo){p_v.tab=[] +for(i=0;i=0){note=s.notes[i] +if(note.b40==b40){note.ti2=true +d+=s.dur/play_factor;return note.tie_ty?do_tie(s,b40,d):d}} +return d} +function gen_grace(s){var g,i,n,t,d,s2,next=s.next +if(s.sappo){d=C.BLEN/16}else if((!next||next.type!=C.NOTE)&&s.prev&&s.prev.type==C.NOTE){d=s.prev.dur/2}else{next.ts_prev.ts_next=next.ts_next;next.ts_next.ts_prev=next.ts_prev;for(s2=next.ts_next;s2;s2=s2.ts_next){if(s2.time!=next.time){next.ts_next=s2 +next.ts_prev=s2.ts_prev;next.ts_prev.ts_next=next;s2.ts_prev=next +break}} +d=next.dur/12 +if(d&(d-1)==0) +d=next.dur/2 +else +d=next.dur/3;next.time+=d;next.dur-=d} +n=0 +for(g=s.extra;g;g=g.next) +if(g.type==C.NOTE) +n++;d/=n*play_factor;t=p_time +for(g=s.extra;g;g=g.next){if(g.type!=C.NOTE) +continue +gen_notes(g,t,d);t+=d}} +function gen_notes(s,t,d){for(var i=0;i<=s.nhd;i++){var note=s.notes[i] +if(note.ti2) +continue +a_e.push(new Float32Array([s.istart,t,instr[s.v],note.midi,note.tie_ty?do_tie(s,note.b40,d):d,1,s.v]))}} +if(!a_e){a_e=[] +abc_time=p_time=0;play_factor=C.BLEN/4*120/60}else if(s.time0){p_time+=dt/play_factor;abc_time=s.time} +switch(s.type){case C.BAR:if(!s.seqst) +break +if(s==rep_en_s){s=rep_nx_s +abc_time=s.time}else if(s.bar_type[0]==':'){rep_nx_s=s +if(!rep_en_s) +rep_en_s=s +if(rep_st_s){s=rep_st_s +play_factor=rep_st_fac}else{s=start;set_voices()} +abc_time=s.time +break} +if(s.bar_type[s.bar_type.length-1]==':'){rep_st_s=s;rep_en_s=null +rep_st_fac=play_factor}else if(s.text&&s.text[0]=='1'){rep_en_s=s} +break +case C.GRACE:if(s.time==0&&abc_time==0){dt=0 +if(s.sappo) +dt=C.BLEN/16 +else if(!s.next||s.next.type!=C.NOTE) +dt=d/2;abc_time-=dt} +gen_grace(s) +break +case C.REST:case C.NOTE:d=s.dur +if(s.next&&s.next.type==C.GRACE){dt=0 +if(s.next.sappo) +dt=C.BLEN/16 +else if(!s.next.next||s.next.next.type!=C.NOTE) +dt=d/2;s.next.time-=dt;d-=dt} +d/=play_factor +if(s.type==C.NOTE) +gen_notes(s,p_time,d) +else +a_e.push(new Float32Array([s.istart,p_time,0,0,d,0,s.v])) +break +case C.BLOCK:switch(s.subtype){case"midictl":a_e.push(new Float32Array([s.istart,p_time,-1,s.ctrl,s.val,1,s.v])) +break +case"midiprog":instr[s.v]=s.instr +break} +break} +s=s.ts_next}}}} +if(typeof module=='object'&&typeof exports=='object') +exports.ToAudio=ToAudio diff --git a/lib/ChordPro/res/abc/abc2svg/tohtml.js b/lib/ChordPro/res/abc/abc2svg/tohtml.js new file mode 100644 index 00000000..2006b130 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/tohtml.js @@ -0,0 +1,320 @@ +// abc2svg - tohtml.js - HTML+SVG generation +// +// Copyright (C) 2014-2023 Jean-Francois Moine +// +// This file is part of abc2svg. +// +// abc2svg is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// abc2svg is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with abc2svg. If not, see . + + var init_done, pw, ml, mr, pkf, lkf, fn, + h_sty = "" + +// replace <>& by XML character references +function clean_txt(txt) { + return txt.replace(/<|>|&.*?;|&/g, function(c) { + switch (c) { + case '<': return "<" + case '>': return ">" + case '&': return "&" + } + return c + }) +} + +abc2svg.abort = function(e) { + if (!init_done) // if empty document + user.img_out('') + abc.blk_flush() + if (typeof abc2svg.printErr == 'function') + abc2svg.printErr(e.message + "\n*** Abort ***\n" + e.stack) + else + abc2svg.print("
" + e.message + "\n*** Abort ***\n" + e.stack + "
") + abc2svg.abc_end() + abc2svg.quit() +} + +function get_date() { + return (new Date()).toUTCString() +} // get_date() + +function header_footer(str) { + var c, d, i, t, + j = 0, + r = ["", "", ""] + + if (str[0] == '"') + str = str.slice(1, -1) + while (1) { + i = str.indexOf('$', j) + if (i < 0) + break + c = str[++i] + switch (c) { + case 'd': + if (!abc2svg.get_mtime) + break // cannot know the modification date of the file + d = abc2svg.get_mtime(abc.parse.fname) + break + case 'D': + d = get_date() + break + case 'F': + d = abc.parse.fname + break + case 'I': + str = str.replace('$', '') + d = str[i] + case 'T': + t = abc.info()[c] + d = t ? t.split('\n', 1)[0] : '' + break + case 'P': + d = '\x0c' // form feed + break + case 'V': + d = "abc2svg-" + abc2svg.version + break + default: + d = '' + break + } + str = str.replace('$' + c, d) + j = i + } + str = str.split('\n') + for (j = 0; j < str.length; j++) { + if (j != 0) + for (i = 0; i < 3; i++) + r[i] += '
' + t = str[j].split('\t') + if (t.length == 1) { + r[1] += t[0] + } else { + for (i = 0; i < 3; i++) { + if (t[i]) + r[i] += t[i] + } + } + } + return r +} // header_footer() + +// set a paragraph style +function set_pstyle() { + var nml, nmr, nlkf, npkf, npw, + cfmt = abc.cfmt(), + psty = '' + + nml = cfmt.leftmargin + if (nml != ml) { + if (ml == undefined) + ml = nml + psty += 'margin-left:' + nml.toFixed(1) + 'px;' + } + nmr = cfmt.rightmargin + if (nmr != mr) { + if (mr == undefined) + mr = nmr + psty += 'margin-right:' + nmr.toFixed(1) + 'px;' + } + nlkf = cfmt.lineskipfac + if (nlkf != lkf) { + if (lkf == undefined) + lkf = nlkf + psty += 'line-height:' + ((nlkf * 100) | 0).toString() + '%;' + } + npkf = cfmt.parskipfac + if (npkf != pkf) { + if (pkf == undefined) + pkf = npkf + psty += 'margin-bottom:' + npkf.toFixed(2) + 'em;' + } + npw = cfmt.pagewidth + if (npw != pw || nml != ml || nmr != mr) { + if (pw == undefined) + pw = npw + psty += 'width:' + (npw - nml - nmr).toFixed(1) + 'px;' + } + return psty +} + +// entry point from cmdline +abc2svg.abc_init = function(args) { + var cfmt = abc.cfmt() + + // output a header or footer + function gen_hf(type, str) { + var i, page, + lcr = ["l", "c", "r"], +//fixme: handle font changes? + a = header_footer(clean_txt(str)) + + abc2svg.print('') + for (i = 0; i < 3; i++) { + str = a[i] + if (!str) + str = ' ' +//fixme + if (str.indexOf('\x0c') >= 0) { + str = str.replace('\x0c', '') + page = " page" + } else { + page = '' + } + abc2svg.print('') + } + abc2svg.print('
' + + str + '
') + } + + user.page_format = true + + // output the html header + user.img_out = function(str) { + if (!str) + return + if (init_done) { + abc2svg.print(str) + return + } + if (/^\n\ +' + fn.replace(/.*\//,'') + + '\n\ +') + if (header || footer) { + if (header) + gen_hf("header", header) + if (footer) + gen_hf("footer", footer) + + abc2svg.print('\ +\n\ + \n\ + \n\ + \n\ +
\n\ +
 
\n\ +
') + init_done = 2 // with header/footer + } else { + init_done = 1 + } + + // output the first generated string + abc2svg.print(str) + } + + // get the main ABC source file name + for (var i = 0; i < args.length; i++) { + var a = args[i] + + if (a[0] == '-') { + i++ + continue + } + fn = a + break + } +} + +abc2svg.abc_end = function() { + var font_style = abc.get_font_style() + + if (!init_done) // if empty document + user.img_out('') + if (user.errtxt) + abc2svg.print("
" + clean_txt(user.errtxt) + "
") + if (font_style) // if some %%text at the end + abc2svg.print('') + if (init_done == 2) // if with header/footer + abc2svg.print('\ +
\n\ +
 
\n\ +
') + abc2svg.print('') +} diff --git a/lib/ChordPro/res/abc/abc2svg/tropt-1.js b/lib/ChordPro/res/abc/abc2svg/tropt-1.js new file mode 100644 index 00000000..66d09876 --- /dev/null +++ b/lib/ChordPro/res/abc/abc2svg/tropt-1.js @@ -0,0 +1,56 @@ +//tropt.js-module to optimize the notes after transposition +if(typeof abc2svg=="undefined") +var abc2svg={} +abc2svg.tropt={set_pitch:function(of,last_s){if(last_s){of(last_s) +return} +var v,p_v,s,m,nt,p,a,np,na,C=abc2svg.C,vo_tb=this.get_voice_tb(),nv=vo_tb.length +function ok(s,p){var nt,m +while(s){if(s.bar_type) +return 1 +if(s.type==C.NOTE){for(m=0;m<=s.nhd;m++){nt=s.notes[m] +if(nt.pit==p) +return nt.acc}} +s=s.next} +return 1} +for(v=0;v= 0 ) { + args.unshift("tohtml.js") +} +else { + args.unshift("toxhtml.js") +} +abc_cmd( "chordproabc", args, "QuickJS" ) diff --git a/lib/ChordPro/res/abc/cmd.js b/lib/ChordPro/res/abc/cmd.js new file mode 100644 index 00000000..027e6a64 --- /dev/null +++ b/lib/ChordPro/res/abc/cmd.js @@ -0,0 +1,91 @@ +// abc2svg - cmd.js - stripped down version of cmdline.js + +// user definitions +var user = { + read_file: function(fn) { // read a file (main or included) + var i, + p = fn, + file = abc2svg.readFile(p) + + if (!file && fn[0] != '/') { + for (i = 0; i < abc2svg.path.length; i++) { + p = abc2svg.path[i] + '/' + fn + file = abc2svg.readFile(p) + if (file) + break + } + } + + if (!file) + return file + + // memorize the file path + i = p.lastIndexOf('/') + if (i > 0) { + p = p.slice(0, i) + if (abc2svg.path.indexOf(p) < 0) + abc2svg.path.unshift(p) + } + + // convert the file content into a Unix string + i = file.indexOf('\r') + if (i >= 0) { + if (file[i + 1] == '\n') + file = file.replace(/\r\n/g, '\n') // M$ + else + fike = file.replace(/\r/g, '\n') // Mac + } + + // load the required modules (synchronous) + abc2svg.modules.load(file) + + return file + }, + errtxt: '', + errmsg: // print or store the error messages + typeof abc2svg.printErr == 'function' + ? function(msg, l, c) { abc2svg.printErr(msg) } + : function(msg, l, c) { user.errtxt += msg + '\n' } +} // user + +var abc // (global for 'toxxx.js') + +abc2svg.path = [] // path to ABC files + +// treat a file +function do_file(fn) { + var file = user.read_file(fn) + + if (!file) { + user.errmsg("Cannot read file '" + fn + "'") + return + } + + // generate + try { + abc.tosvg(fn, file) + } + catch (e) { + abc2svg.abort(e) + } +} // do_file() + +function abc_cmd(cmd, args, interp_name) { + var fn = args[0] + abc2svg.abort = function(e) { + abc2svg.printErr('javascript error: ' + e.message + + '\nStack:\n' + e.stack) + abc2svg.quit() + } // abort() + + // initialize the backend + abc = new abc2svg.Abc(user) + abc2svg.abc_init(args) + do_file(fn) +// abc.tosvg('cmd', '%%select\n') + abc2svg.abc_end() +} + +//Local Variables: +//tab-width: 4 +//End: diff --git a/lib/ChordPro/res/config/chordpro.json b/lib/ChordPro/res/config/chordpro.json index 32927d8c..8e7757da 100644 --- a/lib/ChordPro/res/config/chordpro.json +++ b/lib/ChordPro/res/config/chordpro.json @@ -58,6 +58,10 @@ // Substitute Unicode sharp/flats in chord names. // Will fallback to ChordProSymbols the font doesn't have the glyphs. "truesf" : false, + // Amount of indent for wrapped lines. Actual indent is the stringwidth. + "wrapindent" : "x", + // Flow text. Do not use. + "flowtext" : false, }, // Metadata. @@ -81,10 +85,13 @@ // Globally defined (added) meta data, // This is explicitly NOT intended for the metadata items above. "meta" : { + // Do not remove or change this one. + "_configversion" : [ "6.031" ], }, // Assets. - "assets" : {}, + "assets" : { + }, // Dates. Format is a strftime template. "dates" : { @@ -171,7 +178,7 @@ // "all": all chords used. // "user": only prints user defined chords. // "sorted": order the chords by key. - // "suppress": a series of chord (names) thet will not generate + // "suppress": a series of chord (names) that will not generate // diagrams, e.g. if they are considered trivial. // Note: The type of diagram (string or keyboard) is determined // by the value of "instrument.type". @@ -230,25 +237,17 @@ "type" : "image", "module" : "ABC", "handler" : "abc2svg", + // No longer used -- ./default.abc will always be used if present "config" : "default", // or "none", or "myformat.fmt" - // The preamble is a list of lines inserted before the ABC data. + // The preamble is a list of lines inserted before the ABC data, + // and after the delegate supplied preamble. // DO NOT MODIFY unless you know what you are doing! "preamble" : [ - // Get rid of as much space as possible. - "%%topspace 0", - "%%titlespace 0", - "%%musicspace 0", - "%%composerspace 0", - "%%infospace 0", - "%%textspace 0", - "%%leftmargin 0cm", - "%%rightmargin 0cm", - "%%staffsep 0", // Use ChordPro fonts for lyrics and chords. "%%textfont pdf.fonts.text", "%%gchordfont pdf.fonts.chord", ], - "preprocess" : { "abc" : [], "svg" : [] }, + "preprocess" : { "abc" : [] }, "omit" : false, }, "ly" : { @@ -264,6 +263,12 @@ ], "omit" : false, }, + "svg" : { + "type" : "image", + "module" : "SVG", + "handler" : "svg2svg", + "omit" : false, + }, }, // Definitions for PDF output. @@ -522,11 +527,7 @@ "fontdir" : [], "fontconfig" : { - // alternatives: regular r normal - // alternatives: bold b strong - // alternatives: italic i oblique o emphasis - // alternatives: bolditalic bi italicbold ib boldoblique bo obliquebold ob - "times" : { + "times, serif" : { "" : "Times-Roman", "bold" : "Times-Bold", "italic" : "Times-Italic", @@ -535,10 +536,17 @@ "helvetica" : { "" : "Helvetica", "bold" : "Helvetica-Bold", + // Only helvetica uses oblique, use italic for all other fonts "oblique" : "Helvetica-Oblique", "boldoblique" : "Helvetica-BoldOblique", }, - "courier" : { + "sans, sans-serif" : { + "" : "Helvetica", + "bold" : "Helvetica-Bold", + "italic" : "Helvetica-Oblique", + "bolditalic" : "Helvetica-BoldOblique", + }, + "courier, mono, monospace" : { "" : "Courier", "bold" : "Courier-Bold", "italic" : "Courier-Italic", @@ -685,6 +693,8 @@ "quote" : false, }, }, + // Retain # comments -- we'll output them. + "comments" : "retain", }, // Settings for HTML backend. @@ -751,25 +761,29 @@ // For (debugging (internal use only)). "debug" : { - "chords" : 0, - "config" : 0, - "echo" : 0, - "fonts" : 0, - "images" : 0, - "layout" : 0, - "meta" : 0, - "mma" : 0, - "spacing" : 0, - "song" : 0, - "songfull" : 0, - "csv" : 0, - "abc" : 0, - "ly" : 0, - "svg" : 0, + "a2crd" : 0, + "assets" : 0, + "chords" : 0, + "config" : 0, + "echo" : 0, + "fonts" : 0, + "images" : 0, + "layout" : 0, + "meta" : 0, + "mma" : 0, + "paths" : 0, + "spacing" : 0, + "song" : 0, + "songfull" : 0, + "ops" : 0, + "csv" : 0, + "abc" : 0, + "ly" : 0, + "svg" : 0, // For temporary use. - "x1" : 0, - "x2" : 0, - "x3" : 0, + "x1" : 0, + "x2" : 0, + "x3" : 0, }, } diff --git a/lib/ChordPro/res/config/chordpro.prp b/lib/ChordPro/res/config/chordpro.prp index 0b230ca3..ef003a9b 100644 --- a/lib/ChordPro/res/config/chordpro.prp +++ b/lib/ChordPro/res/config/chordpro.prp @@ -1,16 +1,20 @@ # Configuration for ChordPro. +# Do not remove or change this one. +meta._configversion = [ 6.031 ] + # Includes. These are processed first, before the rest of # the config file. # -# "include" takes a list of either filenames or preset names. +# Takes a list of either filenames or preset names. +# E.g. include = [ modern1 lib/mycfg.json ] include = [ guitar ] -# General settings, to be changed by configs and command line. +# General settings, often changed by configs and command line. settings { # Strict behaviour. strict = true - # Add line info for backend diagnostics. + # Obsolete -- lineinfo is always included. lineinfo = true # Titles flush: default center. titles = center @@ -28,14 +32,18 @@ settings { memorize = false # Chords inline. # May be a string containing pretext %s posttext. - # Defaults to "[%s]" if true. + # Defaults to "[%s]" if set to a value that doesn't contain "%s". inline-chords = false + # Same, for annotations. Ignored unless inline-chords is set. + # Must be a string containing pretext %s posttext. + # Default is "%s". + inline-annotations = %s # Chords under the lyrics. chords-under = false - # Transcoding. - transcode = "" # Transposing. transpose = 0 + # Transcoding. + transcode = "" # Always decapoize. decapo = false # Chords parsing strategy. @@ -43,6 +51,17 @@ settings { chordnames = strict # Allow note names in []. notenames = false + # Always replace chords by their canonical form. + chords-canonical = false + # If false, chorus labels are used as tags. + choruslabels = true + # Substitute Unicode sharp/flats in chord names. + # Will fallback to ChordProSymbols the font doesn't have the glyphs. + truesf = false + # Amount of indent for wrapped lines. Actual indent is the stringwidth. + wrapindent = x + # Flow text. Do not use. + flowtext = false } # Metadata. @@ -51,21 +70,120 @@ settings { # If strict is zero, {meta ...} will accept any key. # Important: "title" and "subtitle" must always be in this list. # The separator is used to concatenate multiple values. +# If autosplit is true, the separator is also used to split +# values upon input. metadata { - keys = [ title subtitle artist composer lyricist arranger - album copyright year sorttitle key - time tempo capo duration + keys = [ title subtitle + artist composer lyricist arranger + album copyright year + sorttitle + key time tempo capo duration ] strict = true separator = "; " + autosplit = true +} +# Globally defined (added) meta data. +# This is explicitly NOT intended for the metadata items above. +meta { +} + +# Assets. +assets { +} + +# Dates. Format is a strftime template. +dates { + today { + format = "%A, %B %e, %Y" + } +} + +# User settings. These are usually set by a separate config file. +# +user { + name = "" + fullname = "" +} + +# Instrument settings. These are usually set by a separate +# config file. +# +instrument { + type = "" + description = "" +} + +# Note (chord root) names. +# Strings and tuning. +tuning = [ E2 A2 D3 G3 B3 E4 ] + +# In case of alternatives, the first one is used for output. +# Note that it is tempting to use real sharps and flats for output, +# but most fonts don't have the glyphs :(. +notes { + flat = [ + C + [ Db Des D♭ ] + D + [ Eb Es Ees E♭ ] + E + F + [ Gb Ges G♭ ] + G + [ Ab As Aes A♭ ] + A + [ Bb Bes B♭ ] + B + ] + sharp = [ + C + [ C# Cis C♯ ] + D + [ D# Dis D♯ ] + E + F + [ F# Fis F♯ ] + G + [ G# Gis G♯ ] + A + [ A# Ais A♯ ] + B + ] + system = common + movable = false +} + +# User defined chords. +# "base" defaults to 1. +# Use 0 for an empty string, and -1 for a muted string. +# "fingers" is optional. +# "display" (optional) can be used to change the way the chord is displayed. +chords = [ + # { + # name = Bb + # base = 1 + # frets = [ 1 1 3 3 3 1 ] + # fingers = [ 1 1 2 3 4 1 ] + # display = "B\x{266d}" + # } + # If the name of the first entry is "defaults" its properties may + # be used as defaults for the rest of the chords. +] + +# Format to show chord names. May contain markup. +chord-formats { + common = %{root|%{}%{qual|%{}}%{ext|%{}}%{bass|/%{}}|%{name}} + roman = %{root|%{}%{qual|%{}}%{ext|%{}}%{bass|/%{}}|%{name}} + nashville = %{root|%{}%{qual|%{}}%{ext|%{}}%{bass|/%{}}|%{name}} } # Printing chord diagrams. -# show= prints the chords used in the song. -# all= all chords used. -# user= only prints user defined chords. -# sorted= order the chords by key. -# suppress= a series of chord (names) that will not generate +# show: prints the chords used in the song. +# all -- all chords used. +# user -- only prints user defined chords. +# sorted: order the chords by key. +# suppress: a series of chord (names) that will not generate # diagrams, e.g. if they are considered trivial. diagrams { show = all @@ -73,7 +191,7 @@ diagrams { suppress = [] } -# Diagnostig messages. +# Diagnostic messages. diagnostics { format = "\"%f\", line %n, %m\n\t%l" } @@ -113,68 +231,88 @@ toc { title = Table of Contents line = %{title} # Sorting order. + # Currently only sorting by page number and alpha is implemented. order = page } -# Intrument definitions. +# Delegates. +# Basically a delegate is a section {start_of_XXX} which content is +# collected and handled later by the delegate module. -instrument = "" -tuning = [ E2 A2 D3 G3 B3 E4 ] -notes { - flat = [ - C - [ Db Des D♭ ] - D - [ Eb Es Ees E♭ ] - E - F - [ Gb Ges G♭ ] - G - [ Ab As Aes A♭ ] - A - [ Bb Bes B♭ ] - B - ] - sharp = [ - C - [ C# Cis C♯ ] - D - [ D# Dis D♯ ] - E - F - [ F# Fis F♯ ] - G - [ G# Gis G♯ ] - A - [ A# Ais A♯ ] - B - ] - system = common - movable = false +delegates { + abc { + type = image + module = ABC + handler = abc2svg + # No longer used -- ./default.abc will always be used if present + config = "default" + # The preamble is a list of lines inserted before the ABC data, + # and after the delegate supplied preamble. + # DO NOT MODIFY unless you know what you are doing! + preamble = [ + # Use ChordPro fonts for lyrics and chords. + "%%textfont pdf.fonts.text" + "%%gchordfont pdf.fonts.chord" + ] + preprocess { + abc = [] + } + omit = false + } + ly { + type = image + module = Lilypond + handler = ly2svg + config = "default" + # The preamble is a list of lines inserted before the lilipond data. + # This is a good place to set the version and global customizations. + preamble = [ + "\version "2.21.0"" + "\header { tagline = ##f }" + ] + omit = false + } + svg { + type = image + module = SVG + handler = svg2svg + omit = false + } } -chords = [] - -# Layout definitions for PDF output. +# Definitions for PDF output. pdf { + # Choose a PDF::API2 compatible library, or leave empty to + # have ChordPro choose one for you. + # Currently supported are "PDF::API2" and "PDF::Builder". + library = "" + + # PDF Properties. Arbitrary key/values may be added. + # Note that the context for substitutions is the first song. + info { + title = %{title} + author = "" + subject = "" + keywords = "" + } + # Papersize, 'a4' or [ 595, 842 ] etc. papersize = a4 theme { # Forgeround color. Usually black. - foreground : black - # Shades of grey + foreground = black + # Shades of grey. # medium is used for pressed keys in keyboard diagrams. - foreground-medium : grey70 + foreground-medium = grey70 # light is used as background for comments, cell bars, ... - foreground-light : grey90 + foreground-light = grey90 # Background color. Usually none or white. - background : none + background = none } - # Space between columns, in pt. columnspace = 20 @@ -197,6 +335,7 @@ pdf { title = 1.2 lyrics = 1.2 chords = 1.2 + diagramchords = 1.2 grid = 1.2 tab = 1 toc = 1.4 @@ -212,18 +351,18 @@ pdf { # Chorus side bar. # Suppress by setting offset and/or width to zero. bar { - offset = 8 - width = 1 - color = foreground + offset = 8 + width = 1 + color = foreground } tag = Chorus # Recall style: Print the tag using the type. # Alternatively quote the lines of the preceding chorus. recall { choruslike = false - tag = Chorus - type = comment - quote = false + tag = Chorus + type = comment + quote = false } } @@ -235,7 +374,7 @@ pdf { # Alignment for the labels. Default is left. align = left # Alternatively, render labels as comments. - # "comment", "comment_italic" or "comment_box", + # Values are "comment", "comment_italic" or "comment_box". comment = "" } @@ -269,6 +408,10 @@ pdf { vspace = 3 vcells = 4 linewidth = 0.1 + barwidth = 0.8 + nutwidth = 5 + dotsize = 0.8 + fingers = 1 } # Keyboard diagrams. @@ -287,7 +430,7 @@ pdf { keys = 14 base = C linewidth = 0.1 - pressed = grey + pressed = foreground-medium hspace = 3.95 vspace = 0.3 } @@ -297,9 +440,17 @@ pdf { # The width and colour of the cell bar lines can be specified. # Suppress the cell bar lines by setting width to 0. cellbar { - width = 1 + width = 0 color = foreground-medium - } + } + show = 1 + symbols { + color = 'blue' + } + volta { + color = 'blue' + span = 0.7 + } } # Even/odd pages. A value of -1 denotes odd/even pages. @@ -307,6 +458,11 @@ pdf { # Align songs to even/odd pages. When greater than 1, force alignment. pagealign-songs = 1 + # PDF file to add as front matter. + front-matter = "" + # PDF file to add as back matter. + back-matter = "" + # Formats. formats { # Titles/Footers. @@ -317,25 +473,28 @@ pdf { # By default, a page has: default { - # No title/subtitle. - title = [ "" "" "" ] - subtitle = [ "" "" "" ] - # Footer is title -- page number. - footer = [ %{title} "" %{page} ] + # No title/subtitle. + title = [ "" "" "" ] + subtitle = [ "" "" "" ] + # Footer is title -- page number. + footer = [ %{title} "" %{page} ] + background = "" } # The first page of a song has: title { - # Title and subtitle. - title = [ "" %{title} "" ] - subtitle = [ "" %{subtitle} "" ] - # Footer with page number. - footer = [ "" "" %{page} ] + # Title and subtitle. + title = [ "" %{title} "" ] + subtitle = [ "" %{subtitle} "" ] + # Footer with page number. + footer = [ "" "" %{page} ] + background = "" } # The very first output page is slightly different: first { - # It has title and subtitle, like normal 'first' pages. - # But no footer. - footer = [ "" "" "" ] + # It has title and subtitle, like normal 'first' pages. + # But no footer. + footer = [ "" "" "" ] + background = "" } } @@ -362,26 +521,26 @@ pdf { # alternatives: bold b strong # alternatives: italic i oblique o emphasis # alternatives: bolditalic bi italicbold ib boldoblique bo obliquebold ob - times { - '' = Times-Roman - bold = Times-Bold - italic = Times-Italic - bolditalic = Times-BoldItalic + "times, serif" { + '' = Times-Roman + bold = Times-Bold + italic = Times-Italic + bolditalic = Times-BoldItalic } - helvetica { - '' = Helvetica - bold = Helvetica-Bold - oblique = Helvetica-Oblique - boldoblique = Helvetica-BoldOblique + "helvetica, sans, sans-serif" { + '' = Helvetica + bold = Helvetica-Bold + oblique = Helvetica-Oblique + boldoblique = Helvetica-BoldOblique } - courier { - '' = Courier - bold = Courier-Bold - italic = Courier-Italic - bolditalic = Courier-BoldItalic + "courier, mono, monospace" { + '' = Courier + bold = Courier-Bold + italic = Courier-Italic + bolditalic = Courier-BoldItalic } dingbats { - "" = ZapfDingbats + "" = ZapfDingbats } } @@ -394,47 +553,46 @@ pdf { fonts { title { - name = Times-Bold - size = 14 + name = Times-Bold + size = 14 } text { - name = Times-Roman - size = 12 + name = Times-Roman + size = 12 } chord { - name = Helvetica-Oblique - size = 10 + name = Helvetica-Oblique + size = 10 } chordfingers { - name = ZapfDingbats - size = 10 - numbercolor = background + file = ChordProSymbols.ttf + numbercolor = background } comment { - name = Helvetica - size = 12 - background = foreground-light + name = Helvetica + size = 12 + background = foreground-light } comment_italic { - name = Helvetica-Oblique - size = 12 + name = Helvetica-Oblique + size = 12 } comment_box { - name = Helvetica - size = 12 - frame = 1 + name = Helvetica + size = 12 + frame = 1 } tab { - name = Courier - size = 10 + name = Courier + size = 10 } toc { - name = Times-Roman - size = 11 + name = Times-Roman + size = 11 } grid { - name = Helvetica - size = 10 + name = Helvetica + size = 10 } } @@ -485,76 +643,104 @@ pdf { } -# Settings for ChordPro backend. -chordpro { - # Style of chorus. - chorus { - # Recall style: Print the tag using the type. - # Alternatively quote the lines of the preceding chorus. - # If no tag+type or quote: use {chorus}. - # Note: Variant 'msp' always uses {chorus}. - recall { - # tag = Chorus - tag = "" - # type = comment - type = "" - quote = false - } - } -} - # Settings for CSV (part of PDF). pdf.csv { fields = [ { - meta = title - name = title + meta = title + name = title } - { - meta = pagerange - name = pages + { + meta = pagerange + name = pages } - { - meta = sorttitle - name = sorttitles + { + meta = sorttitle + name = sort title } - { - meta = artist - name = artists + { + meta = artist + name = artists } - { - meta = composer - name = composers + { + meta = composer + name = composers } - { - meta = collection - name = collections + { + meta = collection + name = collections } - { - meta = actual_key - name = keys + { + meta = key_actual + name = keys } - { - meta = year - name = years + { + meta = year + name = years + } + { + name = my_field + omit = 1 + value = text } - { - name = my_field - omit = 1 - value = text - } ] separator = ; songsonly = 1 vseparator = | } +# Settings for ChordPro backend. +chordpro { + # Style of chorus. + chorus { + # Recall style: Print the tag using the type. + # Alternatively quote the lines of the preceding chorus. + # If no tag+type or quote: use {chorus}. + # Note: Variant 'msp' always uses {chorus}. + recall { + # tag = Chorus + tag = "" + # type = comment + type = "" + quote = false + } + } + comments = retain +} + # Settings for HTML backend. html { # Stylesheet links. styles { - display = chordpro.css - print = chordpro_print.css + display = chordpro.css + print = chordpro_print.css + } +} + +# Settings for LaTeX backend. +latex { + template_include_path = [] + templates { + comment = comment.tt + image = image.tt + songbook = songbook.tt + } +} + +# Settings for Text backend. +text { + # Style of chorus. + chorus { + # Recall style: Print the tag using the type. + # Alternatively quote the lines of the preceding chorus. + # If no tag+type or quote: use {chorus}. + # Note: Variant 'msp' always uses {chorus}. + recall { + # tag = Chorus / type = comment + tag = "" + type = "" + quote = false + } } } @@ -577,49 +763,39 @@ parser { preprocess { # All lines. all = [] - # Directives. - directive = [] - # Song lines (lyrics) only. - songline = [] - } -} - -# Dates. -dates { - today { - format = %A, %B %e, %Y + # Directives. + directive = [] + # Song lines (lyrics) only. + songline = [] } } -# Delegates. -# Basically a delegate is a section {start_of_XXX} which content is -# collected and handled later by the backend. - -delegates { - abc { - type = image - handler = abc2image - } - ly { - type = image - handler = ly2image - } -} # For debugging (internal use only). debug { - abc = false - config = false - csv = false - fonts = false - images = false - layout = false - ly = false - meta = false - mma = false - song = false - songfull = false - spacing = false + a2crd = 0 + abc = 0 + assets = 0 + chords = 0 + config = 0 + csv = 0 + echo = 0 + fonts = 0 + images = 0 + layout = 0 + ly = 0 + meta = 0 + mma = 0 + ops = 0 + paths = 0 + song = 0 + songfull = 0 + spacing = 0 + svg = 0 + # For temporary use. + x1 = 0 + x2 = 0 + x3 = 0 } # End of config. diff --git a/lib/ChordPro/res/config/config.schema b/lib/ChordPro/res/config/config.schema index 6e60160d..381ca0b6 100644 --- a/lib/ChordPro/res/config/config.schema +++ b/lib/ChordPro/res/config/config.schema @@ -1,1800 +1,1870 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "ChordPro Configuration", - "definitions": { - "colorspec": { - "description": "Colour specification.", - "type": "string", - "format": "color", - "pattern": "^(#[0-9A-Fa-f]{6}|[-A-Za-z0-9]+)$" - }, - "delegatespec": { - "description": "Delegates.", - "additionalProperties": false, - "properties": { - "type": { - "type": "string", - "default": "image" - }, - "handler": { - "type": "string", - "default": "abc2image" - }, - "config": { - "type": "string", - "default": "default" - }, - "preamble": { - "type": "array", - "items" : { - "type": "string" - } - }, - "module": { - "type": "string", - "default": "ABC" - }, - "omit": { - "type": "boolean", - "default": false - }, - "preprocess": { - "type": "object" - } - }, - "required" : [ "type", "handler", "module" ], - "defaultProperties" : [ "type", "handler", "module" ] - }, - "fontspec": { - "description": "Font specification.", - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - }, - "file": { - "type": "string" - }, - "description": { - "type": "string" - }, - "size": { - "type": "number" - }, - "color": { - "$ref": "#/definitions/colorspec" - }, - "numbercolor": { - "$ref": "#/definitions/colorspec" - }, - "background": { - "$ref": "#/definitions/colorspec" - }, - "frame": { - "type": [ "boolean", "integer" ] - } - }, - "anyOf": [ - { "required": [ "description" ] }, - { "required": [ "name" ] }, - { "required": [ "file" ] } - ] - }, - "tptspec": { - "description": "Three-part title format specification, left, center, right.", - "type": "array", - "oneOf" : [ - { "items": { "type": "string" }, - "minItems": 3, - "maxItems": 3 - }, - { "items": { - "type": "array", - "items": { "type": "string" }, - "minItems": 3, - "maxItems": 3 - }, - "minItems": 1 - } - ] - } +{ "$schema" : "https://json-schema.org/draft-07/schema", + + "title" : "ChordPro Configuration", + "definitions" : { + "backendspec" : { + "description" : "Standard properties for backends.", + "type" : "object", + "additionalProperties" : true, + "properties" : { + "comments" : { + "description" : "Retain or ignore comments", + "enum" : [ "ignore", "retain" ], + "type" : "string", + "default" : "ignore" + } + } + }, + "colorspec" : { + "description" : "Colour specification.", + "type" : "string", + "format" : "color", + "pattern" : "^(#[0-9A-Fa-f]{6}|[-A-Za-z0-9]+)$" + }, + "delegatespec" : { + "description" : "Delegates.", + "additionalProperties" : false, + "properties" : { + "type" : { + "type" : "string", + "default" : "image" + }, + "handler" : { + "type" : "string", + "default" : "abc2svg" + }, + "config" : { + "type" : "string", + "default" : "default" + }, + "preamble" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "module" : { + "type" : "string", + "default" : "ABC" + }, + "omit" : { + "type" : "boolean", + "default" : false + }, + "preprocess" : { + "type" : "object" + } + }, + "required" : [ "type", "handler", "module" ], + "defaultProperties" : [ "type", "handler", "module" ] + }, + "fontspec" : { + "description" : "Font specification.", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "file" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "size" : { + "type" : "number" + }, + "color" : { + "$ref" : "#/definitions/colorspec" + }, + "numbercolor" : { + "$ref" : "#/definitions/colorspec" + }, + "background" : { + "$ref" : "#/definitions/colorspec" + }, + "frame" : { + "type" : [ "boolean", "integer" ] + } + }, + "anyOf" : [ + { "required" : [ "description" ] }, + { "required" : [ "name" ] }, + { "required" : [ "file" ] } + ] + }, + "tptspec" : { + "description" : "Three-part title format specification, left, center, right.", + "type" : "array", + "oneOf" : [ + { "items" : { "type" : "string" }, + "minItems" : 3, + "maxItems" : 3 + }, + { "items" : { + "type" : "array", + "items" : { "type" : "string" }, + "minItems" : 3, + "maxItems" : 3 + }, + "minItems" : 1 + } + ] + } + }, + + "additionalProperties" : false, + "format" : "categories", + "basicCategoryTitle" : "Settings", + "properties" : { + + "include" : { + "title" : "Includes", + "description" : "List of configs to be processed before this one.", + "type" : "array", + "items" : { + "type" : "string", + "title" : "Config", + "headerTemplate" : "Config {{ i1 }}" + } }, - "additionalProperties": false, - "format": "categories", - "basicCategoryTitle": "Settings", - "properties": { - - "include": { - "title": "Includes", - "description": "List of configs to be processed before this one.", - "type": "array", - "items": { - "type": "string", - "title": "Config", - "headerTemplate": "Config {{ i1 }}" - } - }, - - "settings": { - "title": "General settings.", - "description": "General settings.", - "format": "grid", - "additionalProperties": false, - "properties": { - - "chordnames": { - "description": "Strictness of parsing chord names.", - "type": "string", - "default": "strict", - "enum": [ "strict", "relaxed" ] - }, + "settings" : { + "title" : "General settings.", + "description" : "General settings.", + "format" : "grid", + "additionalProperties" : false, + "properties" : { + + "chordnames" : { + "description" : "Strictness of parsing chord names.", + "type" : "string", + "default" : "strict", + "enum" : [ "strict", "relaxed" ] + }, - "chords-under": { - "description": "", - "title": "Chords under the lyrics.", - "type": "boolean", - "default": false, - "options" : { "infoText" : "settings.chords-under\nNormally, chords are placed above the lyrics.
Check this to place chords under the lyrics instead.", - "title": "Chords under the lyrics" } - }, + "chords-under" : { + "description" : "", + "title" : "Chords under the lyrics.", + "type" : "boolean", + "default" : false, + "options" : { "infoText" : "settings.chords-under\nNormally, chords are placed above the lyrics.
Check this to place chords under the lyrics instead.", + "title" : "Chords under the lyrics" } + }, - "chords-canonical": { - "description": "", - "title": "Use canonical representation for chords.", - "type": "boolean", - "default": false - }, + "chords-canonical" : { + "description" : "", + "title" : "Use canonical representation for chords.", + "type" : "boolean", + "default" : false + }, - "choruslabels": { - "description": "", - "title": "If false, chorus labels are used as tags.", - "type": "boolean", - "default": true - }, + "choruslabels" : { + "description" : "", + "title" : "If false, chorus labels are used as tags.", + "type" : "boolean", + "default" : true + }, - "columns": { - "description": "Number of columns.", - "type": "integer", - "default": 1, - "minimum": 1, - "maximum": 3, - "format" : "number" - }, + "columns" : { + "description" : "Number of columns.", + "type" : "integer", + "default" : 1, + "minimum" : 1, + "maximum" : 3, + "format" : "number" + }, - "decapo": { - "description": "Eliminate capo by transposing chords.", - "type": "boolean", - "default": false - }, + "decapo" : { + "description" : "Eliminate capo by transposing chords.", + "type" : "boolean", + "default" : false + }, - "inline-annotations": { - "description": "Format for inline annotations. Requires inline-chords..", - "type": "string", - "default": "%s" - }, + "inline-annotations" : { + "description" : "Format for inline annotations. Requires inline-chords..", + "type" : "string", + "default" : "%s" + }, - "inline-chords": { - "description": "Chords inline.", - "default": false, - "anyOf": [ - { "type": "string" }, - { "type": "boolean" } - ] - }, + "inline-chords" : { + "description" : "Chords inline.", + "default" : false, + "anyOf" : [ + { "type" : "string" }, + { "type" : "boolean" } + ] + }, - "lineinfo": { - "description": "Retain line info for backend diagnostics.", - "default": true, - "type": "boolean" - }, + "lineinfo" : { + "description" : "Retain line info for backend diagnostics.", + "default" : true, + "type" : "boolean" + }, - "lyrics-only": { - "description": "Suppress chords.", - "type": "boolean", - "default": false - }, + "lyrics-only" : { + "description" : "Suppress chords.", + "type" : "boolean", + "default" : false + }, - "memorize": { - "description": "Memorize the chords from sections.", - "type": "boolean", - "default": false - }, + "memorize" : { + "description" : "Memorize the chords from sections.", + "type" : "boolean", + "default" : false + }, - "notenames": { - "description": "Allow parsing of note names.", - "type": "boolean", - "default": false - }, + "notenames" : { + "description" : "Allow parsing of note names.", + "type" : "boolean", + "default" : false + }, - "strict": { - "description": "Enforce strict interpretation of the input.", - "type": "boolean", - "default": true - }, + "strict" : { + "description" : "Enforce strict interpretation of the input.", + "type" : "boolean", + "default" : true + }, - "suppress-empty-chords": { - "description": "Suppress empty chord lines.", - "type": "boolean", - "default": true - }, + "suppress-empty-chords" : { + "description" : "Suppress empty chord lines.", + "type" : "boolean", + "default" : true + }, - "suppress-empty-lyrics": { - "description": "Suppress blank lyrics lines.", - "type": "boolean", - "default": true - }, + "suppress-empty-lyrics" : { + "description" : "Suppress blank lyrics lines.", + "type" : "boolean", + "default" : true + }, - "titles": { - "description": "Titles flush.", - "type": "string", - "default": "center", - "enum": [ "center", "left", "right" ] - }, + "titles" : { + "description" : "Titles flush.", + "type" : "string", + "default" : "center", + "enum" : [ "center", "left", "right" ] + }, - "transcode": { - "description": "Transcode chords.", - "type": "string", - "default": "" - }, + "transcode" : { + "description" : "Transcode chords.", + "type" : "string", + "default" : "" + }, - "transpose": { - "description": "Transpose chords.", - "type": "integer", - "default": 0, - "minimum" : -12, - "maximum" : 12, - "format" : "number" - }, + "transpose" : { + "description" : "Transpose chords.", + "type" : "integer", + "default" : 0, + "minimum" : -12, + "maximum" : 12, + "format" : "number" + }, - "truesf": { - "description": "Substitute Unicode sharp/flats in chord names.", - "type": "boolean", - "default": false - } - } - }, - - "chord-formats": { - "title": "Chord display formats", - "description": "Format string for rendering chord names.", - "additionalProperties": false, - "properties": { - "common": { - "description": "Format string for rendering common chord names.", - "type": "string", - "default": "%{root|%{}%{qual|%{}}%{ext|%{}}%{bass|/%{}}|%{name}}" - }, - "roman": { - "description": "Format string for rendering roman chord names.", - "type": "string", - "default": "%{root|%{}%{qual|%{}}%{ext|%{}}%{bass|/%{}}|%{name}}" - }, - "nashville": { - "description": "Format string for rendering nashville chord names.", - "type": "string", - "default": "%{root|%{}%{qual|%{}}%{ext|%{}}%{bass|/%{}}|%{name}}" - } - } - }, - - "diagrams": { - "title": "Chord diagrams", - "description": "Chord diagrams.", - "format": "grid", - "additionalProperties": false, - "properties": { - - "show": { - "description": "Show selected or all chord diagrams at end.", - "type": "string", - "enum": [ "all", "user", "none" ], - "default": "all" - }, + "truesf" : { + "description" : "Substitute Unicode sharp/flats in chord names.", + "type" : "boolean", + "default" : false + }, - "sorted": { - "default": false, - "description": "Sort the diagrams.", - "type": "boolean" - }, + "wrapindent" : { + "description" : "Indent for wrapped lyrics.", + "type" : "string", + "default" : "x" + }, - "suppress": { - "default": [], - "description": "Chords (names) that will not generate diagrams, e.g. if they are considered trivial.", - "type": "array", - "items" : { - "type": "string" + "flowtext" : { + "description" : "Considerr text flowed.", + "type" : "boolean", + "default" : false + } + } + }, + + "chord-formats" : { + "title" : "Chord display formats", + "description" : "Format string for rendering chord names.", + "additionalProperties" : false, + "properties" : { + "common" : { + "description" : "Format string for rendering common chord names.", + "type" : "string", + "default" : "%{root|%{}%{qual|%{}}%{ext|%{}}%{bass|/%{}}|%{name}}" + }, + "roman" : { + "description" : "Format string for rendering roman chord names.", + "type" : "string", + "default" : "%{root|%{}%{qual|%{}}%{ext|%{}}%{bass|/%{}}|%{name}}" + }, + "nashville" : { + "description" : "Format string for rendering nashville chord names.", + "type" : "string", + "default" : "%{root|%{}%{qual|%{}}%{ext|%{}}%{bass|/%{}}|%{name}}" + } + } + }, + + "diagrams" : { + "title" : "Chord diagrams", + "description" : "Chord diagrams.", + "format" : "grid", + "additionalProperties" : false, + "properties" : { + + "show" : { + "description" : "Show selected or all chord diagrams at end.", + "type" : "string", + "enum" : [ "all", "user", "none" ], + "default" : "all" + }, + + "sorted" : { + "default" : false, + "description" : "Sort the diagrams.", + "type" : "boolean" + }, + + "suppress" : { + "default" : [], + "description" : "Chords (names) that will not generate diagrams, e.g. if they are considered trivial.", + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + + "chordpro" : { + "title" : "ChordPro (output)", + "description" : "Settings for ChordPro backend.", + "allOf" : [ + { "$ref" : "#/definitions/backendspec" }, + { "properties" : { + "chorus" : { + "title" : "Chorus", + "description" : "Appearance of chorus.", + "format" : "grid-strict", + "additionalProperties" : false, + "properties" : { + "recall" : { + "description" : "Appearance of chorus recall.", + "additionalProperties" : false, + "properties" : { + "quote" : { + "description" : "Quote the chorus.", + "default" : false, + "type" : "boolean" + }, + "tag" : { + "description" : "Label for recalled chorus.", + "default" : "Chorus", + "type" : "string" + }, + "type" : { + "description" : "Type for tag text.", + "default" : "comment", + "enum" : [ + "", + "comment", + "comment_italic", + "comment_box" + ], + "type" : "string" + } + } + }, + "tag" : { + "description" : "(Obsolete) Label for Chorus.", + "type" : "string", + "default" : "Chorus" + } + } + } + } } + ] + }, + + "text" : { + "title" : "Text (output)", + "description" : "Settings for Text backend.", + "allOf" : [ + { "$ref" : "#/definitions/backendspec" }, + { "properties" : { + "chorus" : { + "title" : "Chorus", + "description" : "Appearance of chorus.", + "format" : "grid-strict", + "additionalProperties" : false, + "properties" : { + "recall" : { + "description" : "Appearance of chorus recall.", + "additionalProperties" : false, + "properties" : { + "quote" : { + "description" : "Quote the chorus.", + "default" : false, + "type" : "boolean" + }, + "tag" : { + "description" : "Label for recalled chorus.", + "default" : "Chorus", + "type" : "string" + }, + "type" : { + "description" : "Type for tag text.", + "default" : "comment", + "enum" : [ + "", + "comment", + "comment_italic", + "comment_box" + ], + "type" : "string" } + } + }, + "tag" : { + "description" : "(Obsolete) Label for Chorus.", + "type" : "string", + "default" : "Chorus" } + } } - }, - - "chordpro": { - "title": "ChordPro (output)", - "description": "Settings for ChordPro backend.", - "type": "object", - "additionalProperties": false, - "properties": { - "chorus": { - "title": "Chorus", - "description": "Appearance of chorus.", - "format": "grid-strict", - "additionalProperties": false, - "properties": { - "recall": { - "description": "Appearance of chorus recall.", - "additionalProperties": false, - "properties": { - "quote": { - "description": "Quote the chorus.", - "default": false, - "type": "boolean" - }, - "tag": { - "description": "Label for recalled chorus.", - "default": "Chorus", - "type": "string" - }, - "type": { - "description": "Type for tag text.", - "default": "comment", - "enum": [ - "", - "comment", - "comment_italic", - "comment_box" - ], - "type": "string" - } - } - }, - "tag": { - "description": "(Obsolete) Label for Chorus.", - "type": "string", - "default": "Chorus" - } - } + } } + ] + }, + + "assets" : { + "title" : "Assets", + "description" : "Assets (placeholder).", + "type" : "object", + "additionalProperties" : true + }, + + "a2crd" : { + "title" : "A2Crd (input)", + "description" : "Settings for A2Crd.", + "type" : "object", + "format" : "grid", + "additionalProperties" : false, + "properties" : { + "infer-titles" : { + "description" : "Treat leading lyrics lines as title/subtitle lines.", + "type" : "boolean", + "default" : true + }, + "tabstop" : { + "description" : "Tab width.", + "type" : "integer", + "default" : 8 + }, + "classifier" : { + "description" : "Analysis strategy.", + "type" : "string", + "default" : "pct_chords" + } + } + }, + + "chords" : { + "title" : "Chords", + "description" : "Additional chord definitions.", + "type" : "array", + "items" : { + "additionalProperties" : false, + "properties" : { + "name" : { + "description" : "The name of this chord.", + "type" : "string" + }, + "base" : { + "description" : "The base fret, usually 1.", + "type" : "integer", + "minimum" : 1, + "default" : 1 + }, + "baselabeloffset" : { + "description" : "Displacement for the base fret label.", + "type" : "integer", + "minimum" : 0, + "default" : 0 + }, + "copy" : { + "description" : "Copy another definition.", + "type" : "string" + }, + "display" : { + "description" : "How to show the chord name.", + "type" : "string" + }, + "frets" : { + "description" : "Finger positions from low string to hight string.", + "type" : "array", + "items" : { + "type" : "integer", + "minimum" : -1, + "default" : 0 + } + }, + "fingers" : { + "description" : "Fingers from low string to hight string.", + "type" : "array", + "items" : { + "type" : "integer", + "minimum" : -1, + "default" : 0 + } + }, + "easy" : { + "description" : "Deprecated.", + "type" : "boolean", + "default" : true + } + }, + "anyOf" : [ + { "required" : [ "name", "frets" ] }, + { "required" : [ "name", "copy" ] } + ] + } + }, + + "contents" : { + "description" : "Tables of contents.", + "type" : "array", + "items" : { + "additionalProperties" : false, + "properties" : { + "fields" : { + "description" : "The metadata for this toc.", + "type" : "array", + "items" : { + "type" : "string", + "minimum" : 1 + } + }, + "fold" : { + "description" : "For future use.", + "type" : "boolean", + "default" : false + }, + "label" : { + "description" : "The label (title) for this toc.", + "type" : "string", + "default" : "Table of Contents" + }, + "line" : { + "description" : "The format for the toc lines.", + "type" : "string" + }, + "omit" : { + "description" : "Omit this toc.", + "type" : "boolean", + "default" : false + }, + "pageno" : { + "description" : "The format for the page numbers in the toc.", + "type" : "string", + "default" : "%{page}" + } + } + } + }, + + "diagnostics" : { + "title" : "Diagnostics", + "description" : "Diagnostics.", + "additionalProperties" : false, + "properties" : { + "format" : { + "description" : "Format for error messages.", + "type" : "string", + "default" : "\"%f\", line %n, %m\n\t%l" + } + } + }, + + "html" : { + "title" : "HTML", + "description" : "Settings for HTML output.", + "allOf" : [ + { "$ref" : "#/definitions/backendspec" }, + { "properties" : { + "styles" : { + "description" : "Stylesheets.", + "additionalProperties" : false, + "properties" : { + "display" : { + "title" : "Stylesheet for screen.", + "description" : "Stylesheet for screen.", + "type" : "string", + "default" : "chordpro.css" + }, + "print" : { + "title" : "Stylesheet for printing.", + "description" : "Stylesheet for printing.", + "type" : "string", + "default" : "chordpro_print.css" } + } } - }, - - "text": { - "title": "Text (output)", - "description": "Settings for Text backend.", - "type": "object", - "additionalProperties": false, - "properties": { - "chorus": { - "title": "Chorus", - "description": "Appearance of chorus.", - "format": "grid-strict", - "additionalProperties": false, - "properties": { - "recall": { - "description": "Appearance of chorus recall.", - "additionalProperties": false, - "properties": { - "quote": { - "description": "Quote the chorus.", - "default": false, - "type": "boolean" - }, - "tag": { - "description": "Label for recalled chorus.", - "default": "Chorus", - "type": "string" - }, - "type": { - "description": "Type for tag text.", - "default": "comment", - "enum": [ - "", - "comment", - "comment_italic", - "comment_box" - ], - "type": "string" - } - } - }, - "tag": { - "description": "(Obsolete) Label for Chorus.", - "type": "string", - "default": "Chorus" - } - } + } } + ] + }, + + "instrument" : { + "title" : "Instrument", + "description" : "Description of the instrument. This is usually set from an included instrument config.", + "propertyOrder" : 2020, + "type" : "object", + "additionalProperties" : false, + "properties" : { + "description" : { + "description" : "Descriptive instrument name.", + "type" : "string", + "default" : "" + }, + "type" : { + "description" : "Instrument type.", + "type" : "string", + "default" : "" + } + } + }, + + "meta" : { + "description" : "User defined metadata items.", + "additionalProperties" : { + "type" : "object" + }, + "properties" : { + "_configversion" : { + "description" : "Config file version.", + "type" : "array", + "uniqueItems" : true, + "items" : { + "type" : "string" + } + } + } + }, + + "metadata" : { + "title" : "Metadata", + "propertyOrder" : 2010, + "description" : "The list of metadata items.", + "type" : "object", + "additionalProperties" : false, + "properties" : { + "autosplit" : { + "description" : "Split data on separator.", + "type" : "boolean", + "default" : true + }, + "keys" : { + "description" : "Known metadata items.", + "type" : "array", + "uniqueItems" : true, + "additionalItems" : true, + "items" : { + "type" : "string" + }, + "default" : [ + "title", "subtitle", + "artist", "composer", "lyricist", "arranger", + "album", "copyright", "year", + "key", "time", "tempo", "capo", "duration" + ] + }, + "separator" : { + "description" : "Separator when joining metadata items.", + "type" : "string", + "default" : "; " + }, + "strict" : { + "description" : "Disallow extending the list of metadata items.", + "type" : "boolean", + "default" : true + } + } + }, + + "notes" : { + "title" : "Note system", + "description" : "Note system.", + "additionalProperties" : false, + "properties" : { + "system" : { + "description" : "The note system used.", + "type" : "string", + "default" : "common" + }, + "movable" : { + "description" : "Movable note system.", + "type" : "boolean", + "default" : false + }, + "sharp" : { + "description" : "Note names, using sharps.", + "type" : "array", + "format" : "table", + "uniqueItems" : true, + "items" : { + "type" : [ "string", "array" ], + "uniqueItems" : true, + "items" : { "type" : "string" } + } + }, + "flat" : { + "description" : "Note names, using flats.", + "type" : "array", + "format" : "table", + "uniqueItems" : true, + "items" : { + "type" : [ "string", "array" ], + "uniqueItems" : true, + "items" : { "type" : "string" } + } + } + } + }, + + "dates" : { + "description" : "Date formats.", + "additionalProperties" : false, + "properties" : { + "today" : { + "description" : "Today's date.", + "additionalProperties" : false, + "properties" :{ + "format" : { + "description" : "Format", + "type" : "string", + "default" : "%A, %B %e, %Y" + } + } + } + } + }, + + "delegates" : { + "description" : "Delegates.", + "type" : "object", + "additionalProperties" : false, + "properties" : { + "abc" : { + "description" : "Embedding ABC", + "allOf" : [ + { "$ref" : "#/definitions/delegatespec" }, + { "properties" : { + "handler" : { + "type" : "string", + "default" : "abc2svg" + }, + "module" : { + "type" : "string", + "default" : "ABC" + } + } } + ] + }, + "ly" : { + "description" : "Embedding Lilypond", + "allOf" : [ + { "$ref" : "#/definitions/delegatespec" }, + { "properties" : { + "handler" : { + "type" : "string", + "default" : "ly2svg" + }, + "module" : { + "type" : "string", + "default" : "Lilypond" + } + } } + ] + }, + "svg" : { + "description" : "Embedding SVG", + "allOf" : [ + { "$ref" : "#/definitions/delegatespec" }, + { "properties" : { + "handler" : { + "type" : "string", + "default" : "svg2svg" + }, + "module" : { + "type" : "string", + "default" : "SVG" + } + } } + ] + } + } + }, + + "latex" : { + "title" : "LaTeX backend", + "description" : "", + "allOf" : [ + { "$ref" : "#/definitions/backendspec" }, + { "properties" : { + "template_include_path" : { + "description" : "Include paths for templates.", + "additionalProperties" : false, + "type" : "array" + }, + "templates" : { + "description" : "Templates for LaTeX generation.", + "additionalProperties" : false, + "properties" : { + "comment" : { + "description" : "Helper template to render comments.", + "type" : "string", + "default" : "comment.tt" + }, + "image" : { + "description" : "Helper template to render images.", + "type" : "string", + "default" : "image.tt" + }, + "songbook" : { + "description" : "Master template to render the songbook.", + "type" : "string", + "default" : "songbook.tt" } + } } - }, - - "assets": { - "title": "Assets", - "description": "Assets (placeholder).", - "type": "object", - "additionalProperties": true - }, - - "a2crd": { - "title": "A2Crd (input)", - "description": "Settings for A2Crd.", - "type": "object", - "format" : "grid", - "additionalProperties": false, - "properties": { - "infer-titles": { - "description": "Treat leading lyrics lines as title/subtitle lines.", - "type": "boolean", - "default": true - }, - "tabstop": { - "description": "Tab width.", - "type": "integer", - "default": 8 - }, - "classifier": { - "description": "Analysis strategy.", - "type": "string", - "default": "pct_chords" - } + } } + ] + }, + + "parser" : { + "title" : "Preprocessing", + "description" : "Preprocessing the input.", + "type" : "object", + "additionalProperties" : false, + "properties" : { + "preprocess" : { + "description" : "", + "additionalProperties" : false, + "properties" : { + "all" : { + "description" : "", + "type" : "array" + }, + "directive" : { + "description" : "", + "type" : "array" + }, + "songline" : { + "description" : "", + "type" : "array" } - }, - - "chords": { - "title": "Chords", - "description": "Additional chord definitions.", - "type": "array", - "items": { - "additionalProperties": false, - "properties": { - "name": { - "description": "The name of this chord.", - "type": "string" - }, - "base": { - "description": "The base fret, usually 1.", - "type": "integer", - "minimum": 1, - "default": 1 - }, - "baselabeloffset": { - "description": "Displacement for the base fret label.", - "type": "integer", - "minimum": 0, - "default": 0 - }, - "copy": { - "description": "Copy another definition.", - "type": "string" - }, - "display": { - "description": "How to show the chord name.", - "type": "string" - }, - "frets": { - "description": "Finger positions from low string to hight string.", - "type": "array", - "items": { - "type": "integer", - "minimum": -1, - "default": 0 - } + } + } + } + }, + + "pdf" : { + "title" : "PDF", + "description" : "Settings for PDF output.", + "allOf" : [ + { "$ref" : "#/definitions/backendspec" }, + { "properties" : { + + "back-matter" : { + "default" : "", + "description" : "Back page(s) filename.", + "type" : "string" + }, + + "capoheading" : { + "description" : "Value for Capo heading when using chordscolumn.", + "type" : "string", + "default" : "" + }, + + "chordscolumn" : { + "description" : "Chords position in alternative formatting, if greater than 0.", + "default" : 0, + "minimum" : 0, + "type" : "number" + }, + + "chorus" : { + "description" : "Appearance of chorus.", + "format" : "grid-strict", + "additionalProperties" : false, + "properties" : { + "bar" : { + "description" : "Appearance of side bar.", + "additionalProperties" : false, + "properties" : { + "color" : { + "description" : "Bar colour.", + "allOf" : [ + { "$ref" : "#/definitions/colorspec" }, + { "default" : "foreground" } + ] }, - "fingers": { - "description": "Fingers from low string to hight string.", - "type": "array", - "items": { - "type": "integer", - "minimum": -1, - "default": 0 - } + "offset" : { + "description" : "Bar offset. Suppress when zero", + "default" : 8, + "minimum" : 0, + "type" : "number" }, - "easy": { - "description": "Deprecated.", - "type": "boolean", - "default": true + "width" : { + "description" : "Bar width. Suppress when zero.", + "default" : 1, + "minimum" : 0, + "type" : "number" } + } }, - "anyOf" : [ - { "required": [ "name", "frets" ] }, - { "required": [ "name", "copy" ] } - ] - } - }, - - "contents": { - "description": "Tables of contents.", - "type": "array", - "items": { - "additionalProperties": false, - "properties": { - "fields": { - "description": "The metadata for this toc.", - "type": "array", - "items": { - "type": "string", - "minimum": 1 - } + "indent" : { + "description" : "Chorus indent.", + "default" : 0, + "minimum" : 0, + "type" : "number" + }, + "recall" : { + "description" : "Appearance of chorus recall.", + "additionalProperties" : false, + "properties" : { + "choruslike" : { + "description" : "Quote the chorus like a chorus.", + "default" : false, + "type" : "boolean" }, - "fold": { - "description": "For future use.", - "type": "boolean", - "default": false + "quote" : { + "description" : "Quote the chorus.", + "default" : false, + "type" : "boolean" }, - "label": { - "description": "The label (title) for this toc.", - "type": "string", - "default": "Table of Contents" + "tag" : { + "description" : "Label for recalled chorus.", + "default" : "Chorus", + "type" : "string" }, - "line": { - "description": "The format for the toc lines.", - "type": "string" - }, - "omit": { - "description": "Omit this toc.", - "type": "boolean", - "default": false - }, - "pageno": { - "description": "The format for the page numbers in the toc.", - "type": "string", - "default": "%{page}" - } - } - } - }, - - "diagnostics": { - "title": "Diagnostics", - "description": "Diagnostics.", - "additionalProperties": false, - "properties": { - "format": { - "description": "Format for error messages.", - "type": "string", - "default": "\"%f\", line %n, %m\n\t%l" - } - } - }, - - "html": { - "title": "HTML", - "description": "Settings for HTML output.", - "type": "object", - "additionalProperties": false, - "properties": { - "styles": { - "description": "Stylesheets.", - "additionalProperties": false, - "properties": { - "display": { - "title": "Stylesheet for screen.", - "description": "Stylesheet for screen.", - "type": "string", - "default": "chordpro.css" - }, - "print": { - "title": "Stylesheet for printing.", - "description": "Stylesheet for printing.", - "type": "string", - "default": "chordpro_print.css" - } + "type" : { + "description" : "Font for tag text.", + "default" : "comment", + "enum" : [ + "comment", + "title", + "text", + "chord", + "tab", + "toc" + ], + "type" : "string" } + } + }, + "tag" : { + "description" : "Label for Chorus.", + "type" : "string", + "default" : "Chorus" } - } - }, - - "instrument": { - "title": "Instrument", - "description": "Description of the instrument. This is usually set from an included instrument config.", - "propertyOrder" : 2020, - "type" : "object", - "additionalProperties": false, - "properties" : { - "description" : { - "description" : "Descriptive instrument name.", - "type": "string", - "default": "" - }, - "type" : { - "description" : "Instrument type.", - "type": "string", - "default": "" - } - } - }, + } + }, - "meta": { - "description": "User defined metadata items.", - "additionalProperties": { - "type" : "object" - } - }, - - "metadata": { - "title": "Metadata", - "propertyOrder" : 2010, - "description": "The list of metadata items.", - "type": "object", - "additionalProperties": false, - "properties": { - "autosplit": { - "description": "Split data on separator.", - "type": "boolean", - "default": true - }, - "keys": { - "description": "Known metadata items.", - "type": "array", - "uniqueItems": true, - "additionalItems": true, - "items": { - "type": "string" + "columnspace" : { + "default" : 20, + "description" : "Distance between columns in multi-column mode.", + "minimum" : 0, + "type" : "number" + }, + + "csv" : { + "description" : "CSV properties.", + "additionalProperties" : false, + "properties" : { + + "fields" : { + "description" : "The fields for the CSV.", + "type" : "array", + "items" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "meta" : { + "type" : "string" + }, + "value" : { + "type" : "string" + }, + "omit" : { + "type" : "boolean", + "default" : false + } }, - "default": [ - "title", "subtitle", - "artist", "composer", "lyricist", "arranger", - "album", "copyright", "year", - "key", "time", "tempo", "capo", "duration" + "required" : [ "name" ], + "anyOf" : [ + { "required" : [ "meta" ] }, + { "required" : [ "value" ] } ] + } }, - "separator": { - "description": "Separator when joining metadata items.", - "type": "string", - "default": "; " + "separator" : { + "description" : "Separator to join field values.", + "type" : "string", + "default" : ";" }, - "strict": { - "description": "Disallow extending the list of metadata items.", - "type": "boolean", - "default": true - } - } - }, - - "notes" : { - "title" : "Note system", - "description" : "Note system.", - "additionalProperties": false, - "properties" : { - "system" : { - "description" : "The note system used.", - "type" : "string", - "default" : "common" - }, - "movable" : { - "description" : "Movable note system.", - "type" : "boolean", - "default" : false - }, - "sharp" : { - "description" : "Note names, using sharps.", - "type" : "array", - "format" : "table", - "uniqueItems" : true, - "items" : { - "type" : [ "string", "array" ], - "uniqueItems": true, - "items": { "type": "string" } - } - }, - "flat" : { - "description" : "Note names, using flats.", - "type" : "array", - "format" : "table", - "uniqueItems" : true, - "items" : { - "type" : [ "string", "array" ], - "uniqueItems": true, - "items": { "type": "string" } - } - } - } - }, - - "dates": { - "description": "Date formats.", - "additionalProperties": false, - "properties": { - "today": { - "description": "Today's date.", - "additionalProperties": false, - "properties":{ - "format": { - "description": "Format", - "type": "string", - "default": "%A, %B %e, %Y" - } - } - } - } - }, - - "delegates": { - "description": "Delegates.", - "type": "object", - "additionalProperties": false, - "properties": { - "abc": { - "description": "Embedding ABC", - "allOf": [ - { "$ref": "#/definitions/delegatespec" }, - { "properties": { - "handler": { - "type": "string", - "default": "abc2image" - }, - "module": { - "type": "string", - "default": "ABC" - } - } } - ] - }, - "ly": { - "description": "Embedding Lilypond", - "allOf": [ - { "$ref": "#/definitions/delegatespec" }, - { "properties": { - "handler": { - "type": "string", - "default": "ly2image" - }, - "module": { - "type": "string", - "default": "Lilypond" - } - } } - ] - } - } - }, - - "latex": { - "title": "LaTeX backend", - "description": "", - "type": "object", - "additionalProperties": false, - "properties": { - "template_include_path": { - "description": "Include paths for templates.", - "additionalProperties": false, - "type" : "array" + "vseparator" : { + "description" : "Separator to join meta values.", + "type" : "string", + "default" : "|" }, - "templates" : { - "description": "Templates for LaTeX generation.", - "additionalProperties": false, - "properties": { - "comment": { - "description": "Helper template to render comments.", - "type": "string", - "default" : "comment.tt" - }, - "image": { - "description": "Helper template to render images.", - "type": "string", - "default" : "image.tt" - }, - "songbook": { - "description": "Master template to render the songbook.", - "type": "string", - "default" : "songbook.tt" - } - } + "songsonly" : { + "description" : "Include only songs in the CSV.", + "default" : true, + "type" : "boolean" } - } - }, - - "parser": { - "title": "Preprocessing", - "description": "Preprocessing the input.", - "type": "object", - "additionalProperties": false, - "properties": { - "preprocess": { - "description": "", - "additionalProperties": false, - "properties": { - "all": { - "description": "", - "type": "array" - }, - "directive": { - "description": "", - "type": "array" - }, - "songline": { - "description": "", - "type": "array" - } - } - } - } - }, - - "pdf": { - "title": "PDF", - "description": "Settings for PDF output.", - "type": "object", - "additionalProperties": false, - "properties": { - - "back-matter": { - "default": "", - "description": "Back page(s) filename.", - "type": "string" - }, + } + }, - "capoheading": { - "description": "Value for Capo heading when using chordscolumn.", - "type": "string", - "default": "" - }, - - "chordscolumn": { - "description": "Chords position in alternative formatting, if greater than 0.", - "default": 0, - "minimum": 0, - "type": "number" - }, + "diagrams" : { + "description" : "Appearance of chord diagrams.", + "additionalProperties" : false, + "properties" : { + "fingers" : { + "description" : "Show finger settings, if available.", + "default" : "true", + "type" : [ "boolean", "string" ] + }, + "show" : { + "description" : "Where to show the diagrams.", + "default" : "bottom", + "enum" : [ "top", "right", "below", "bottom" ], + "type" : "string" + }, + "height" : { + "description" : "Distance between frets.", + "default" : 6, + "minimum" : 0, + "type" : "number" + }, + "dotsize" : { + "description" : "Size of the fret dot, fraction of cell width.", + "default" : 0.8, + "minimum" : 0, + "type" : "number" + }, + "barwidth" : { + "description" : "Thickness of bars, fraction of dot width.", + "default" : 0.8, + "minimum" : 0, + "type" : "number" + }, + "width" : { + "description" : "Distance between strings.", + "default" : 6, + "minimum" : 0, + "type" : "number" + }, + "hspace" : { + "description" : "Horizontal space between diagrams.", + "default" : 3.95, + "minimum" : 0, + "type" : "number" + }, + "linewidth" : { + "description" : "Thickness of the diagram lines as a fraction of 'width'.", + "default" : 0.1, + "minimum" : 0, + "type" : "number" + }, + "nutwidth" : { + "description" : "Thickness of the top nut, in 'linewidth'.", + "default" : 5, + "minimum" : 0, + "type" : "number" + }, + "vcells" : { + "description" : "The number of frets shown.", + "default" : 4, + "minimum" : 3, + "type" : "number" + }, + "vspace" : { + "description" : "Vertical space between diagrams.", + "default" : 3, + "minimum" : 0, + "type" : "number" + } + } + }, - "chorus": { - "description": "Appearance of chorus.", - "format": "grid-strict", - "additionalProperties": false, - "properties": { - "bar": { - "description": "Appearance of side bar.", - "additionalProperties": false, - "properties": { - "color": { - "description": "Bar colour.", - "allOf": [ - { "$ref": "#/definitions/colorspec" }, - { "default": "foreground" } - ] - }, - "offset": { - "description": "Bar offset. Suppress when zero", - "default": 8, - "minimum": 0, - "type": "number" - }, - "width": { - "description": "Bar width. Suppress when zero.", - "default": 1, - "minimum": 0, - "type": "number" - } - } - }, - "indent": { - "description": "Chorus indent.", - "default": 0, - "minimum": 0, - "type": "number" - }, - "recall": { - "description": "Appearance of chorus recall.", - "additionalProperties": false, - "properties": { - "choruslike": { - "description": "Quote the chorus like a chorus.", - "default": false, - "type": "boolean" - }, - "quote": { - "description": "Quote the chorus.", - "default": false, - "type": "boolean" - }, - "tag": { - "description": "Label for recalled chorus.", - "default": "Chorus", - "type": "string" - }, - "type": { - "description": "Font for tag text.", - "default": "comment", - "enum": [ - "comment", - "title", - "text", - "chord", - "tab", - "toc" - ], - "type": "string" - } - } - }, - "tag": { - "description": "Label for Chorus.", - "type": "string", - "default": "Chorus" - } - } - }, + "diagramscolumn" : { + "description" : "Chords diagrams are printed in a right column. Value is the column offset.", + "default" : 0, + "minimum" : 0, + "type" : "number" + }, - "columnspace": { - "default": 20, - "description": "Distance between columns in multi-column mode.", - "minimum": 0, - "type": "number" - }, + "even-odd-pages" : { + "default" : 1, + "description" : "Even/odd pages (1), odd/even (-1) or odd (0).", + "enum" : [ 1, -1, 0 ], + "options" : { + "enum_titles" : [ "Even/Odd", "Odd/Even", "Odd" ] + }, + "type" : "integer" + }, - "csv" : { - "description": "CSV properties.", - "additionalProperties": false, - "properties": { - - "fields": { - "description": "The fields for the CSV.", - "type": "array", - "items": { - "type" : "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - }, - "meta": { - "type": "string" - }, - "value": { - "type": "string" - }, - "omit": { - "type": "boolean", - "default": false - } - }, - "required": [ "name" ], - "anyOf": [ - { "required": [ "meta" ] }, - { "required": [ "value" ] } - ] - } - }, - "separator": { - "description": "Separator to join field values.", - "type": "string", - "default" : ";" - }, - "vseparator": { - "description": "Separator to join meta values.", - "type": "string", - "default" : "|" - }, - "songsonly": { - "description": "Include only songs in the CSV.", - "default": true, - "type": "boolean" - } - } - }, + "fontdir" : { + "description" : "The location of font files.", + "type" : "array", + "items" : { + "type" : "string" + }, + "minItems" : 0 + }, - "diagrams": { - "description": "Appearance of chord diagrams.", - "additionalProperties": false, - "properties": { - "fingers": { - "description": "Show finger settings, if available.", - "default": "true", - "type": [ "boolean", "string" ] - }, - "show": { - "description": "Where to show the diagrams.", - "default": "bottom", - "enum": [ "top", "right", "below", "bottom" ], - "type": "string" - }, - "height": { - "description": "Distance between frets.", - "default": 6, - "minimum": 0, - "type": "number" - }, - "dotsize": { - "description": "Size of the fret dot, fraction of cell width.", - "default": 0.8, - "minimum": 0, - "type": "number" - }, - "barwidth": { - "description": "Thickness of bars, fraction of dot width.", - "default": 0.8, - "minimum": 0, - "type": "number" - }, - "width": { - "description": "Distance between strings.", - "default": 6, - "minimum": 0, - "type": "number" - }, - "hspace": { - "description": "Horizontal space between diagrams.", - "default": 3.95, - "minimum": 0, - "type": "number" - }, - "linewidth": { - "description": "Thickness of the diagram lines as a fraction of 'width'.", - "default": 0.1, - "minimum": 0, - "type": "number" - }, - "nutwidth": { - "description": "Thickness of the top nut, in 'linewidth'.", - "default": 5, - "minimum": 0, - "type": "number" - }, - "vcells": { - "description": "The number of frets shown.", - "default": 4, - "minimum": 3, - "type": "number" - }, - "vspace": { - "description": "Vertical space between diagrams.", - "default": 3, - "minimum": 0, - "type": "number" - } - } - }, + "fontconfig" : { + "description" : "The (physical) fonts used for typesetting.", + "type" : "object" + }, - "diagramscolumn": { - "description": "Chords diagrams are printed in a right column. Value is the column offset.", - "default": 0, - "minimum": 0, - "type": "number" - }, + "fonts" : { + "description" : "The (logical) fonts used for typesetting.", + "properties" : { + "annotation" : { + "description" : "Font for annotations.", + "allOf" : [ + { "$ref" : "#/definitions/fontspec" }, + { "properties" : { + "name" : { + "default" : "Helvetica-Oblique", + "type" : "string" + }, + "size" : { + "default" : 10, + "type" : "number" + } + } } + ] + }, + "chord" : { + "description" : "Font for typesetting chord names.", + "allOf" : [ + { "$ref" : "#/definitions/fontspec" }, + { "properties" : { + "name" : { + "default" : "Helvetica-Oblique", + "type" : "string" + }, + "size" : { + "default" : 10, + "type" : "number" + } + } } + ] + }, + "chordfingers" : { + "description" : "Font for numbered chord dots.", + "allOf" : [ + { "$ref" : "#/definitions/fontspec" }, + { "properties" : { + "name" : { + "default" : "ZapfDingpats", + "type" : "string" + }, + "size" : { + "default" : 10, + "type" : "number" + } + } } + ] + }, + "comment" : { + "description" : "Font for typesetting comment text.", + "allOf" : [ + { "$ref" : "#/definitions/fontspec" }, + { "properties" : { + "name" : { + "default" : "Helvetica", + "type" : "string" + }, + "size" : { + "default" : 12, + "type" : "number" + } + } } + ] + }, + "tab" : { + "description" : "Font for typesetting tabs.", + "allOf" : [ + { "$ref" : "#/definitions/fontspec" }, + { "properties" : { + "name" : { + "default" : "Courier", + "type" : "string" + }, + "size" : { + "default" : 10, + "type" : "number" + } + } } + ] + }, + "text" : { + "description" : "Font for typesetting lyrics.", + "allOf" : [ + { "$ref" : "#/definitions/fontspec" }, + { "properties" : { + "name" : { + "default" : "Times-Roman", + "type" : "string" + }, + "size" : { + "default" : 12, + "type" : "number" + } + } } + ] + }, + "title" : { + "description" : "Font for typesetting titles.", + "allOf" : [ + { "$ref" : "#/definitions/fontspec" }, + { "properties" : { + "name" : { + "default" : "Times-Bold", + "type" : "string" + }, + "size" : { + "default" : 14, + "type" : "number" + } + } } + ] + }, + "toc" : { + "description" : "Font for typesetting the table of contents.", + "allOf" : [ + { "$ref" : "#/definitions/fontspec" }, + { "properties" : { + "name" : { + "default" : "Times-Roman", + "type" : "string" + }, + "size" : { + "default" : 11, + "type" : "number" + } + } } + ] + } + }, + "patternProperties" : { + "^(grid|subtitle|comment_italic|comment_box|grid_margin|footer|empty|diagram|diagram_base)$" : + { "$ref" : "#/definitions/fontspec" } + } + }, - "even-odd-pages": { - "default": 1, - "description": "Even/odd pages (1), odd/even (-1) or odd (0).", - "enum": [ 1, -1, 0 ], - "options": { - "enum_titles": [ "Even/Odd", "Odd/Even", "Odd" ] - }, - "type": "integer" - }, + "footspace" : { + "description" : "Space for page footers.", + "default" : 20, + "minimum" : 0, + "type" : "number" + }, - "fontdir": { - "description": "The location of font files.", - "type": "array", - "items" : { - "type" : "string" + "formats" : { + "description" : "Formats for page headers and footers.", + "additionalProperties" : false, + "properties" : { + "default" : { + "description" : "Default properties for all pages.", + "additionalProperties" : false, + "properties" : { + "background" : { + "description" : "Background page.", + "type" : "string" + }, + "title" : { + "allOf" : [ + { "$ref" : "#/definitions/tptspec" }, + { "default" : "" } + ] + }, + "subtitle" : { + "allOf" : [ + { "$ref" : "#/definitions/tptspec" }, + { "default" : "" } + ] }, - "minItems" : 0 + "footer" : { + "allOf" : [ + { "$ref" : "#/definitions/tptspec" }, + { "default" : [ "%{title}", "", "%{page}" ] } + ] + } + } }, - - "fontconfig": { - "description": "The (physical) fonts used for typesetting.", - "type": "object" - }, - - "fonts": { - "description": "The (logical) fonts used for typesetting.", - "properties": { - "annotation": { - "description": "Font for annotations.", - "allOf": [ - { "$ref": "#/definitions/fontspec" }, - { "properties": { - "name": { - "default": "Helvetica-Oblique", - "type": "string" - }, - "size": { - "default": 10, - "type": "number" - } - } } ] - }, - "chord": { - "description": "Font for typesetting chord names.", - "allOf": [ - { "$ref": "#/definitions/fontspec" }, - { "properties": { - "name": { - "default": "Helvetica-Oblique", - "type": "string" - }, - "size": { - "default": 10, - "type": "number" - } - } } ] - }, - "chordfingers": { - "description": "Font for numbered chord dots.", - "allOf": [ - { "$ref": "#/definitions/fontspec" }, - { "properties": { - "name": { - "default": "ZapfDingpats", - "type": "string" - }, - "size": { - "default": 10, - "type": "number" - } - } } ] - }, - "comment": { - "description": "Font for typesetting comment text.", - "allOf": [ - { "$ref": "#/definitions/fontspec" }, - { "properties": { - "name": { - "default": "Helvetica", - "type": "string" - }, - "size": { - "default": 12, - "type": "number" - } - } } ] - }, - "tab": { - "description": "Font for typesetting tabs.", - "allOf": [ - { "$ref": "#/definitions/fontspec" }, - { "properties": { - "name": { - "default": "Courier", - "type": "string" - }, - "size": { - "default": 10, - "type": "number" - } - } } ] - }, - "text": { - "description": "Font for typesetting lyrics.", - "allOf": [ - { "$ref": "#/definitions/fontspec" }, - { "properties": { - "name": { - "default": "Times-Roman", - "type": "string" - }, - "size": { - "default": 12, - "type": "number" - } - } } ] - }, - "title": { - "description": "Font for typesetting titles.", - "allOf": [ - { "$ref": "#/definitions/fontspec" }, - { "properties": { - "name": { - "default": "Times-Bold", - "type": "string" - }, - "size": { - "default": 14, - "type": "number" - } - } } ] - }, - "toc": { - "description": "Font for typesetting the table of contents.", - "allOf": [ - { "$ref": "#/definitions/fontspec" }, - { "properties": { - "name": { - "default": "Times-Roman", - "type": "string" - }, - "size": { - "default": 11, - "type": "number" - } - } } ] - } + "title" : { + "description" : "Properties for per-song title pages.", + "additionalProperties" : false, + "properties" : { + "background" : { + "description" : "Background page.", + "type" : "string" }, - "patternProperties": { - "^(grid|subtitle|comment_italic|comment_box|grid_margin|footer|empty|diagram|diagram_base)$": - { "$ref": "#/definitions/fontspec" } + "title" : { + "allOf" : [ + { "$ref" : "#/definitions/tptspec" }, + { "default" : [ "", "%{title}", "" ] } + ] + }, + "subtitle" : { + "allOf" : [ + { "$ref" : "#/definitions/tptspec" }, + { "default" : [ "", "%{subtitle}", "" ] } + ] + }, + "footer" : { + "allOf" : [ + { "$ref" : "#/definitions/tptspec" }, + { "default" : [ "", "", "%{page}" ] } + ] } + } }, + "first" : { + "description" : "Properties of the very first page.", + "additionalProperties" : false, + "properties" : { + "background" : { + "description" : "Background page.", + "type" : "string" + }, + "title" : { + "description" : "Defaults to default.", + "$ref" : "#/definitions/tptspec" + }, + "subtitle" : { + "description" : "Defaults to default.", + "$ref" : "#/definitions/tptspec" + }, + "footer" : { + "allOf" : [ + { "$ref" : "#/definitions/tptspec" }, + { "default" : "" } + ] + } + } + } + } + }, - "footspace": { - "description": "Space for page footers.", - "default": 20, - "minimum": 0, - "type": "number" - }, + "front-matter" : { + "default" : "", + "description" : "Cover page(s) filename.", + "type" : "string" + }, - "formats": { - "description": "Formats for page headers and footers.", - "additionalProperties": false, - "properties": { - "default": { - "description": "Default properties for all pages.", - "additionalProperties": false, - "properties": { - "background": { - "description": "Background page.", - "type": "string" - }, - "title": { - "allOf": [ - { "$ref": "#/definitions/tptspec" }, - { "default": "" } - ] }, - "subtitle": { - "allOf": [ - { "$ref": "#/definitions/tptspec" }, - { "default": "" } - ] }, - "footer": { - "allOf": [ - { "$ref": "#/definitions/tptspec" }, - { "default": [ "%{title}", "", "%{page}" ] } - ] } - } - }, - "title": { - "description": "Properties for per-song title pages.", - "additionalProperties": false, - "properties": { - "background": { - "description": "Background page.", - "type": "string" - }, - "title": { - "allOf": [ - { "$ref": "#/definitions/tptspec" }, - { "default": [ "", "%{title}", "" ] } - ] }, - "subtitle": { - "allOf": [ - { "$ref": "#/definitions/tptspec" }, - { "default": [ "", "%{subtitle}", "" ] } - ] }, - "footer": { - "allOf": [ - { "$ref": "#/definitions/tptspec" }, - { "default": [ "", "", "%{page}" ] } - ] } - } - }, - "first": { - "description": "Properties of the very first page.", - "additionalProperties": false, - "properties": { - "background": { - "description": "Background page.", - "type": "string" - }, - "title": { - "description": "Defaults to default.", - "$ref": "#/definitions/tptspec" - }, - "subtitle": { - "description": "Defaults to default.", - "$ref": "#/definitions/tptspec" - }, - "footer": { - "allOf": [ - { "$ref": "#/definitions/tptspec" }, - { "default": "" } - ] } - } - } + "grids" : { + "description" : "Grid line properties.", + "additionalProperties" : false, + "properties" : { + "cellbar" : { + "additionalProperties" : false, + "properties" : { + "color" : { + "description" : "Colour of the cell bar", + "$ref" : "#/definitions/colorspec", + "default" : "foreground-light" + }, + "width" : { + "description" : "Width of the cell bar.", + "type" : "integer", + "default" : 1 } + } }, - - "front-matter": { - "default": "", - "description": "Cover page(s) filename.", - "type": "string" - }, - - "grids" : { - "description": "Grid line properties.", - "additionalProperties": false, - "properties" : { - "cellbar" : { - "additionalProperties": false, - "properties": { - "color": { - "description": "Colour of the cell bar", - "$ref": "#/definitions/colorspec", - "default" : "foreground-light" - }, - "width": { - "description": "Width of the cell bar.", - "type": "integer", - "default": 1 - } - } - }, - "show" : { - "description": "Show grid context in output.", - "type": "boolean", - "default" : true - }, - "symbols" : { - "additionalProperties": false, - "properties": { - "color": { - "description": "Colour of the cell bar", - "$ref": "#/definitions/colorspec", - "default" : "foreground-light" - } - } - }, - "volta" : { - "additionalProperties": false, - "properties": { - "color": { - "description": "Colour of the cell bar", - "$ref": "#/definitions/colorspec", - "default" : "foreground-light" - }, - "span" : { - "description": "Volta span (fraction of measure).", - "type": "number", - "default": 0.7 - } - } - } - } + "show" : { + "description" : "Show grid context in output.", + "type" : "boolean", + "default" : true + }, + "symbols" : { + "additionalProperties" : false, + "properties" : { + "color" : { + "description" : "Colour of the cell bar", + "$ref" : "#/definitions/colorspec", + "default" : "foreground-light" + } + } }, + "volta" : { + "additionalProperties" : false, + "properties" : { + "color" : { + "description" : "Colour of the cell bar", + "$ref" : "#/definitions/colorspec", + "default" : "foreground-light" + }, + "span" : { + "description" : "Volta span (fraction of measure).", + "type" : "number", + "default" : 0.7 + } + } + } + } + }, - "head-first-only": { - "description": "Headers and footers only on the very first page.", - "default": false, - "type": "boolean" - }, + "head-first-only" : { + "description" : "Headers and footers only on the very first page.", + "default" : false, + "type" : "boolean" + }, - "headspace": { - "description": "Space for page titles.", - "default": 60, - "minimum": 0, - "type": "number" - }, + "headspace" : { + "description" : "Space for page titles.", + "default" : 60, + "minimum" : 0, + "type" : "number" + }, - "info" : { - "description": "PDF info data.", - "additionalProperties": true, - "properties": { - "author": { - "description": "Name of the author.", - "default": "", - "type": "string" - }, - "keywords": { - "description": "Document keywords.", - "default": "", - "type": "string" - }, - "subject": { - "description": "Document subjecy.", - "default": "", - "type": "string" - }, - "title": { - "description": "Document title.", - "default": "%{title}", - "type": "string" - } - } - - }, + "info" : { + "description" : "PDF info data.", + "additionalProperties" : true, + "properties" : { + "author" : { + "description" : "Name of the author.", + "default" : "", + "type" : "string" + }, + "keywords" : { + "description" : "Document keywords.", + "default" : "", + "type" : "string" + }, + "subject" : { + "description" : "Document subjecy.", + "default" : "", + "type" : "string" + }, + "title" : { + "description" : "Document title.", + "default" : "%{title}", + "type" : "string" + } + } + }, - "kbdiagrams": { - "description": "Appearance of keyboard diagrams.", - "additionalProperties": false, - "properties": { - "show": { - "description": "Where to show the diagrams.", - "default": "bottom", - "enum": [ "top", "right", "below", "bottom" ], - "type": "string" - }, - "height": { - "description": "Height of the diagram.", - "default": 20, - "minimum": 0, - "type": "number" - }, - "width": { - "description": "Width of a single (white) key.", - "default": 4, - "minimum": 0, - "type": "number" - }, - "hspace": { - "description": "Horizontal space between diagrams.", - "default": 3.95, - "minimum": 0, - "type": "number" - }, - "keys": { - "description": "The number of white keys shown.", - "default": 14, - "minimum": 7, - "maximum": 21, - "type": "number" - }, - "base": { - "description": "The leftmost white key.", - "default": "C", - "maximum": 21, - "type": "string", - "enum": [ "C", "F" ] - }, - "linewidth": { - "description": "Thickness of the diagram lines as a fraction of 'width'.", - "default": 0.1, - "minimum": 0, - "type": "number" - }, - "pressed": { - "description": "Color of the 'pressed' keys.", - "default": "foreground-medium", - "type": "string", - "format" : "color" - }, - "vspace": { - "description": "Vertical space between diagrams.", - "default": 3, - "minimum": 0, - "type": "number" - } - } - }, + "kbdiagrams" : { + "description" : "Appearance of keyboard diagrams.", + "additionalProperties" : false, + "properties" : { + "show" : { + "description" : "Where to show the diagrams.", + "default" : "bottom", + "enum" : [ "top", "right", "below", "bottom" ], + "type" : "string" + }, + "height" : { + "description" : "Height of the diagram.", + "default" : 20, + "minimum" : 0, + "type" : "number" + }, + "width" : { + "description" : "Width of a single (white) key.", + "default" : 4, + "minimum" : 0, + "type" : "number" + }, + "hspace" : { + "description" : "Horizontal space between diagrams.", + "default" : 3.95, + "minimum" : 0, + "type" : "number" + }, + "keys" : { + "description" : "The number of white keys shown.", + "default" : 14, + "minimum" : 7, + "maximum" : 21, + "type" : "number" + }, + "base" : { + "description" : "The leftmost white key.", + "default" : "C", + "maximum" : 21, + "type" : "string", + "enum" : [ "C", "F" ] + }, + "linewidth" : { + "description" : "Thickness of the diagram lines as a fraction of 'width'.", + "default" : 0.1, + "minimum" : 0, + "type" : "number" + }, + "pressed" : { + "description" : "Color of the 'pressed' keys.", + "default" : "foreground-medium", + "type" : "string", + "format" : "color" + }, + "vspace" : { + "description" : "Vertical space between diagrams.", + "default" : 3, + "minimum" : 0, + "type" : "number" + } + } + }, - "labels": { - "description": "Margin labels.", - "additionalProperties": false, - "properties": { - "align": { - "description": "Labels text alignment.", - "default": "left", - "enum": [ "left", "right", "center" ], - "type": "string" - }, - "width": { - "description": "Margin width.", - "default": 0, - "type": [ "number", "string" ] - }, - "comment": { - "description": "Render labels as comments.", - "default": "", - "enum": [ - "comment", - "comment_italic", - "comment_box", - "" - ], - "type": "string", - "default": "" - } - } - }, + "labels" : { + "description" : "Margin labels.", + "additionalProperties" : false, + "properties" : { + "align" : { + "description" : "Labels text alignment.", + "default" : "left", + "enum" : [ "left", "right", "center" ], + "type" : "string" + }, + "width" : { + "description" : "Margin width.", + "default" : 0, + "type" : [ "number", "string" ] + }, + "comment" : { + "description" : "Render labels as comments.", + "default" : "", + "enum" : [ + "comment", + "comment_italic", + "comment_box", + "" + ], + "type" : "string", + "default" : "" + } + } + }, - "library": { - "description": "PDF handling library.", - "type": "string" - }, + "library" : { + "description" : "PDF handling library.", + "type" : "string" + }, - "marginbottom": { - "description": "Page bottom margin, excluding footspace.", - "default": 40, - "minimum": 0, - "type": "number" - }, + "marginbottom" : { + "description" : "Page bottom margin, excluding footspace.", + "default" : 40, + "minimum" : 0, + "type" : "number" + }, - "marginleft": { - "description": "Page left margin.", - "default": 40, - "minimum": 0, - "type": "number" - }, + "marginleft" : { + "description" : "Page left margin.", + "default" : 40, + "minimum" : 0, + "type" : "number" + }, - "marginright": { - "description": "Page right margin.", - "default": 40, - "minimum": 0, - "type": "number" - }, + "marginright" : { + "description" : "Page right margin.", + "default" : 40, + "minimum" : 0, + "type" : "number" + }, - "margintop": { - "description": "Page top marging, excluding headspace.", - "default": 80, - "minimum": 0, - "type": "number" - }, + "margintop" : { + "description" : "Page top marging, excluding headspace.", + "default" : 80, + "minimum" : 0, + "type" : "number" + }, - "outlines": { - "description": "PDF outlines (index).", - "type": "array", - "items": { - "additionalProperties": false, - "properties": { - "collapse": { - "description": "Initial display is collapsed.", - "type": "boolean", - "default": false - }, - "fields": { - "description": "The metadata for this outline.", - "type": "array", - "items": { - "type": "string", - "minimum": 1 - } - }, - "fold": { - "description": "For future use.", - "type": "boolean", - "default": false - }, - "label": { - "description": "The label (title) for this outline.", - "type": "string", - "default": "Table of Contents" - }, - "letter": { - "description": "Make letter level if more entries than this value.", - "type": "integer", - "default": 5 - }, - "line": { - "description": "The format for the outline entries.", - "type": "string" - }, - "omit": { - "description": "Omit this outline.", - "type": "boolean", - "default": false - } - } + "outlines" : { + "description" : "PDF outlines (index).", + "type" : "array", + "items" : { + "additionalProperties" : false, + "properties" : { + "collapse" : { + "description" : "Initial display is collapsed.", + "type" : "boolean", + "default" : false + }, + "fields" : { + "description" : "The metadata for this outline.", + "type" : "array", + "items" : { + "type" : "string", + "minimum" : 1 } - }, + }, + "fold" : { + "description" : "For future use.", + "type" : "boolean", + "default" : false + }, + "label" : { + "description" : "The label (title) for this outline.", + "type" : "string", + "default" : "Table of Contents" + }, + "letter" : { + "description" : "Make letter level if more entries than this value.", + "type" : "integer", + "default" : 5 + }, + "line" : { + "description" : "The format for the outline entries.", + "type" : "string" + }, + "omit" : { + "description" : "Omit this outline.", + "type" : "boolean", + "default" : false + } + } + } + }, - "pagealign-songs": { - "default": true, - "description": "Page alignment for songs.", - "type": [ "boolean", "integer" ] - }, + "pagealign-songs" : { + "default" : true, + "description" : "Page alignment for songs.", + "type" : [ "boolean", "integer" ] + }, - "papersize": { - "description": "Output page size, e.g. \"a4\" or [595,842].", - "default": "a4", - "type": [ "string", "array" ], - "items": { "type": "number" }, - "minItems": 2, - "maxItems": 2 - }, + "papersize" : { + "description" : "Output page size, e.g. \"a4\" or [595,842].", + "default" : "a4", + "type" : [ "string", "array" ], + "items" : { "type" : "number" }, + "minItems" : 2, + "maxItems" : 2 + }, - "showlayout": { - "description": "Show the page layout structure.", - "default": false, - "type": "boolean" - }, + "showlayout" : { + "description" : "Show the page layout structure.", + "default" : false, + "type" : "boolean" + }, - "spacing": { - "description": "Baseline distances as a factor of the font size.", - "additionalProperties": false, - "properties": { - "chords": { - "description": "Spacing for chord names.", - "default": 1.2, - "type": "number" - }, - "diagramchords": { - "description": "Spacing for diagram chords.", - "default": 1.2, - "type": "number" - }, - "empty": { - "description": "Spacing for empty (blank) lines.", - "default": 1, - "type": "number" - }, - "grid": { - "description": "Spacing for grid lines.", - "default": 1.2, - "type": "number" - }, - "lyrics": { - "description": "SPacing for lyrics.", - "default": 1.2, - "type": "number" - }, - "tab": { - "description": "Spacing for tab lines.", - "default": 1, - "type": "number" - }, - "title": { - "description": "Spacing for page titles.", - "default": 1.2, - "type": "number" - }, - "toc": { - "description": "Spacing for table of contens lines.", - "default": 1.4, - "type": "number" - } - } - }, + "spacing" : { + "description" : "Baseline distances as a factor of the font size.", + "additionalProperties" : false, + "properties" : { + "chords" : { + "description" : "Spacing for chord names.", + "default" : 1.2, + "type" : "number" + }, + "diagramchords" : { + "description" : "Spacing for diagram chords.", + "default" : 1.2, + "type" : "number" + }, + "empty" : { + "description" : "Spacing for empty (blank) lines.", + "default" : 1, + "type" : "number" + }, + "grid" : { + "description" : "Spacing for grid lines.", + "default" : 1.2, + "type" : "number" + }, + "lyrics" : { + "description" : "SPacing for lyrics.", + "default" : 1.2, + "type" : "number" + }, + "tab" : { + "description" : "Spacing for tab lines.", + "default" : 1, + "type" : "number" + }, + "title" : { + "description" : "Spacing for page titles.", + "default" : 1.2, + "type" : "number" + }, + "toc" : { + "description" : "Spacing for table of contens lines.", + "default" : 1.4, + "type" : "number" + } + } + }, - "split-marker": { - "description": "Marker when syllables are too small for chord.", - "default": [ "", "", "" ], - "type": [ "array", "string" ], - "items": { "type": "string" }, - "minItems": 3, - "maxItems": 3 - }, - - "theme": { - "description": "Theme", - "additionalProperties": false, - "properties": { - "background": { - "description": "Background colour", - "allOf": [ - { "$ref": "#/definitions/colorspec" }, - { "default": "none" } - ] - }, - "foreground": { - "description": "Foreground colour", - "allOf": [ - { "$ref": "#/definitions/colorspec" }, - { "default": "black" } - ] - }, - "foreground-medium": { - "description": "Light foreground colour", - "allOf": [ - { "$ref": "#/definitions/colorspec" }, - { "default": "grey70" } - ] - }, - "foreground-light": { - "description": "Very light foreground colour", - "allOf": [ - { "$ref": "#/definitions/colorspec" }, - { "default": "grey90" } - ] - } - } - }, + "split-marker" : { + "description" : "Marker when syllables are too small for chord.", + "default" : [ "", "", "" ], + "type" : [ "array", "string" ], + "items" : { "type" : "string" }, + "minItems" : 3, + "maxItems" : 3 + }, - "titles-directive-ignore": { - "description": "Ignore titles directives in the songs.", - "default": false, - "type": "boolean" - } - } - }, - - "toc": { - "description": "(Obsolete) Table of Contents", - "additionalProperties": false, - "properties": { - "line": { - "description": "Format for toc lines", - "default": "%{title}", - "type": "string" - }, - "order": { - "description": "Sorting order for song titles", - "enum": [ "page", "alpha" ], - "default": "page", - "type": "string" - }, - "title": { - "description": "Title for the Table of Contents", - "default": "Table of Contents", - "type": "string" - } - } - }, - - "tuning": { - "title": "Tuning", - "description": "Definition of the strings for this instrument. This is usually set from an included instrument config.
Note that string 1 is the highest string.", - "propertyOrder": 2030, - "type": "array", - "items": { - "type": "string", - "title": "String", - "headerTemplate": "String {{ i1 }}", - "pattern": "^[A-G][b#]?[1-9]$" - } - }, - - "user": { - "description": "User data.", - "additionalProperties": false, - "properties" : { - "fullname" : { - "description" : "Full user name", - "type": "string", - "default": "" - }, - "name" : { - "description" : "Short user name.", - "type": "string", - "default": "" - } - } - }, - - "debug": { - "description": "Miscellaneous debug settings.", - "format": "grid", - "additionalProperties": true, - "properties": { - "abc": { - "type": "integer", - "default": 0 - }, - "chords": { - "type": "integer", - "default": 0 - }, - "config": { - "type": "integer", - "default": 0 - }, - "csv": { - "type": "integer", - "default": 0 - }, - "fonts": { - "type": "integer", - "default": 0 - }, - "images": { - "type": "integer", - "default": 0 - }, - "layout": { - "type": "integer", - "default": 0 - }, - "ly": { - "type": "integer", - "default": 0 - }, - "meta": { - "type": "integer", - "default": 0 - }, - "mma": { - "type": "integer", - "default": 0 - }, - "song": { - "type": "integer", - "default": 0 - }, - "songfull": { - "type": "integer", - "default": 0 - }, - "spacing": { - "type": "integer", - "default": 0 + "theme" : { + "description" : "Theme", + "additionalProperties" : false, + "properties" : { + "background" : { + "description" : "Background colour", + "allOf" : [ + { "$ref" : "#/definitions/colorspec" }, + { "default" : "none" } + ] + }, + "foreground" : { + "description" : "Foreground colour", + "allOf" : [ + { "$ref" : "#/definitions/colorspec" }, + { "default" : "black" } + ] + }, + "foreground-medium" : { + "description" : "Light foreground colour", + "allOf" : [ + { "$ref" : "#/definitions/colorspec" }, + { "default" : "grey70" } + ] + }, + "foreground-light" : { + "description" : "Very light foreground colour", + "allOf" : [ + { "$ref" : "#/definitions/colorspec" }, + { "default" : "grey90" } + ] } + } + }, + + "titles-directive-ignore" : { + "description" : "Ignore titles directives in the songs.", + "default" : false, + "type" : "boolean" } - } + } } + ] + }, + + "toc" : { + "description" : "(Obsolete) Table of Contents", + "additionalProperties" : false, + "properties" : { + "line" : { + "description" : "Format for toc lines", + "default" : "%{title}", + "type" : "string" + }, + "order" : { + "description" : "Sorting order for song titles", + "enum" : [ "page", "alpha" ], + "default" : "page", + "type" : "string" + }, + "title" : { + "description" : "Title for the Table of Contents", + "default" : "Table of Contents", + "type" : "string" + } + } + }, + + "tuning" : { + "title" : "Tuning", + "description" : "Definition of the strings for this instrument. This is usually set from an included instrument config.
Note that string 1 is the highest string.", + "propertyOrder" : 2030, + "type" : "array", + "items" : { + "type" : "string", + "title" : "String", + "headerTemplate" : "String {{ i1 }}", + "pattern" : "^[A-G][b#]?[1-9]$" + } + }, + + "user" : { + "description" : "User data.", + "additionalProperties" : false, + "properties" : { + "fullname" : { + "description" : "Full user name", + "type" : "string", + "default" : "" + }, + "name" : { + "description" : "Short user name.", + "type" : "string", + "default" : "" + } + } + }, + + "debug" : { + "description" : "Miscellaneous debug settings.", + "format" : "grid", + "additionalProperties" : true, + "properties" : { + "abc" : { + "type" : "integer", + "default" : 0 + }, + "chords" : { + "type" : "integer", + "default" : 0 + }, + "config" : { + "type" : "integer", + "default" : 0 + }, + "csv" : { + "type" : "integer", + "default" : 0 + }, + "fonts" : { + "type" : "integer", + "default" : 0 + }, + "images" : { + "type" : "integer", + "default" : 0 + }, + "layout" : { + "type" : "integer", + "default" : 0 + }, + "ly" : { + "type" : "integer", + "default" : 0 + }, + "meta" : { + "type" : "integer", + "default" : 0 + }, + "mma" : { + "type" : "integer", + "default" : 0 + }, + "song" : { + "type" : "integer", + "default" : 0 + }, + "songfull" : { + "type" : "integer", + "default" : 0 + }, + "spacing" : { + "type" : "integer", + "default" : 0 + } + } } + } } diff --git a/lib/ChordPro/res/pod/Config.pod b/lib/ChordPro/res/pod/Config.pod index abcf32ac..3cb0e69f 100644 --- a/lib/ChordPro/res/pod/Config.pod +++ b/lib/ChordPro/res/pod/Config.pod @@ -85,6 +85,10 @@ extensive details and examples. // Substitute Unicode sharp/flats in chord names. // Will fallback to ChordProSymbols the font doesn't have the glyphs. "truesf" : false, + // Amount of indent for wrapped lines. Actual indent is the stringwidth. + "wrapindent" : "x", + // Flow text. Do not use. + "flowtext" : false, }, // Metadata. @@ -108,10 +112,13 @@ extensive details and examples. // Globally defined (added) meta data, // This is explicitly NOT intended for the metadata items above. "meta" : { + // Do not remove or change this one. + "_configversion" : [ "6.031" ], }, // Assets. - "assets" : {}, + "assets" : { + }, // Dates. Format is a strftime template. "dates" : { @@ -198,7 +205,7 @@ extensive details and examples. // "all": all chords used. // "user": only prints user defined chords. // "sorted": order the chords by key. - // "suppress": a series of chord (names) thet will not generate + // "suppress": a series of chord (names) that will not generate // diagrams, e.g. if they are considered trivial. // Note: The type of diagram (string or keyboard) is determined // by the value of "instrument.type". @@ -257,25 +264,17 @@ extensive details and examples. "type" : "image", "module" : "ABC", "handler" : "abc2svg", + // No longer used -- ./default.abc will always be used if present "config" : "default", // or "none", or "myformat.fmt" - // The preamble is a list of lines inserted before the ABC data. + // The preamble is a list of lines inserted before the ABC data, + // and after the delegate supplied preamble. // DO NOT MODIFY unless you know what you are doing! "preamble" : [ - // Get rid of as much space as possible. - "%%topspace 0", - "%%titlespace 0", - "%%musicspace 0", - "%%composerspace 0", - "%%infospace 0", - "%%textspace 0", - "%%leftmargin 0cm", - "%%rightmargin 0cm", - "%%staffsep 0", // Use ChordPro fonts for lyrics and chords. "%%textfont pdf.fonts.text", "%%gchordfont pdf.fonts.chord", ], - "preprocess" : { "abc" : [], "svg" : [] }, + "preprocess" : { "abc" : [] }, "omit" : false, }, "ly" : { @@ -291,6 +290,12 @@ extensive details and examples. ], "omit" : false, }, + "svg" : { + "type" : "image", + "module" : "SVG", + "handler" : "svg2svg", + "omit" : false, + }, }, // Definitions for PDF output. @@ -549,11 +554,7 @@ extensive details and examples. "fontdir" : [], "fontconfig" : { - // alternatives: regular r normal - // alternatives: bold b strong - // alternatives: italic i oblique o emphasis - // alternatives: bolditalic bi italicbold ib boldoblique bo obliquebold ob - "times" : { + "times, serif" : { "" : "Times-Roman", "bold" : "Times-Bold", "italic" : "Times-Italic", @@ -562,10 +563,17 @@ extensive details and examples. "helvetica" : { "" : "Helvetica", "bold" : "Helvetica-Bold", + // Only helvetica uses oblique, use italic for all other fonts "oblique" : "Helvetica-Oblique", "boldoblique" : "Helvetica-BoldOblique", }, - "courier" : { + "sans, sans-serif" : { + "" : "Helvetica", + "bold" : "Helvetica-Bold", + "italic" : "Helvetica-Oblique", + "bolditalic" : "Helvetica-BoldOblique", + }, + "courier, mono, monospace" : { "" : "Courier", "bold" : "Courier-Bold", "italic" : "Courier-Italic", @@ -712,6 +720,8 @@ extensive details and examples. "quote" : false, }, }, + // Retain # comments -- we'll output them. + "comments" : "retain", }, // Settings for HTML backend. @@ -778,25 +788,29 @@ extensive details and examples. // For (debugging (internal use only)). "debug" : { - "chords" : 0, - "config" : 0, - "echo" : 0, - "fonts" : 0, - "images" : 0, - "layout" : 0, - "meta" : 0, - "mma" : 0, - "spacing" : 0, - "song" : 0, - "songfull" : 0, - "csv" : 0, - "abc" : 0, - "ly" : 0, - "svg" : 0, + "a2crd" : 0, + "assets" : 0, + "chords" : 0, + "config" : 0, + "echo" : 0, + "fonts" : 0, + "images" : 0, + "layout" : 0, + "meta" : 0, + "mma" : 0, + "paths" : 0, + "spacing" : 0, + "song" : 0, + "songfull" : 0, + "ops" : 0, + "csv" : 0, + "abc" : 0, + "ly" : 0, + "svg" : 0, // For temporary use. - "x1" : 0, - "x2" : 0, - "x3" : 0, + "x1" : 0, + "x2" : 0, + "x3" : 0, }, } diff --git a/pp/common/ChordPro_Bundle.pm b/pp/common/ChordPro_Bundle.pm index a2d7b812..3b829917 100644 --- a/pp/common/ChordPro_Bundle.pm +++ b/pp/common/ChordPro_Bundle.pm @@ -5,12 +5,14 @@ use ChordPro::Output::Common; use ChordPro::Output::PDF; use ChordPro::Output::PDF::PDFWriter; -use ChordPro::Output::PDF::StringDiagrams; +use ChordPro::Output::PDF::StringDiagram; +use ChordPro::Output::PDF::KeyboardDiagram; use ChordPro::Output::Debug; use ChordPro::Output::ChordPro; use ChordPro::Output::Text; use ChordPro::Output::HTML; use ChordPro::Delegate::ABC; use ChordPro::Delegate::Lilypond; +use ChordPro::Delegate::SVG; 1; diff --git a/pp/common/Text_Layout_Bundle.pm b/pp/common/Text_Layout_Bundle.pm index 761f72b6..003930ab 100644 --- a/pp/common/Text_Layout_Bundle.pm +++ b/pp/common/Text_Layout_Bundle.pm @@ -4,4 +4,6 @@ use Text::Layout; use Text::Layout::FontConfig; use Text::Layout::FontDescriptor; use Text::Layout::PDFAPI2; +use Text::Layout::PDFAPI2::ImageElement; +use Text::Layout::ElementRole; use Text::Layout::Version; diff --git a/pp/common/chordpro.pp b/pp/common/chordpro.pp index 6b669af8..834d7d9f 100644 --- a/pp/common/chordpro.pp +++ b/pp/common/chordpro.pp @@ -35,6 +35,12 @@ --module=XS::Parse::Keyword --module=XS::Parse::Sublike +# Same for JavaScript::QuickJS +--module=JavaScript::QuickJS +--module=JavaScript::QuickJS::Function +--module=JavaScript::QuickJS::Date +--module=JavaScript::QuickJS::RegExp + # Resources. --addfile=../../lib/ChordPro/res;res diff --git a/pp/docker/Dockerfile b/pp/docker/Dockerfile index ef8a9a61..708efdce 100644 --- a/pp/docker/Dockerfile +++ b/pp/docker/Dockerfile @@ -25,23 +25,19 @@ RUN apt-get install --no-install-recommends -y \ libpdf-api2-perl \ libobject-pad-perl \ libimage-info-perl \ - abcm2ps \ - imagemagick \ - librsvg2-bin \ - libimage-magick-perl \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # Perl modules from CPAN. RUN cpanm \ + JavaScript::QuickJS \ File::LoadLines \ String::Interpolate::Named \ HarfBuzz::Shaper \ - Text::Layout + Text::Layout \ + Data::Printer -FROM base - -# Add ChordPro +FROM base AS chordpro-prod RUN cpanm chordpro # Cleanup. @@ -50,3 +46,18 @@ RUN rm -fr .cpanminus # Setup for run-time ENV DOCKER_PACKAGED=1.00 RUN chordpro --about + +FROM base AS chordpro-dev +COPY chordpro-dev.tar.gz abc2svg_qjs.tar.gz ./ +RUN mkdir ChordPro \ + && tar xf chordpro-dev.tar.gz -C ChordPro \ + && cd ChordPro \ + && perl Makefile.PL \ + && make && make install + +# Cleanup. +RUN rm -fr .cpanminus + +# Setup for run-time +ENV DOCKER_PACKAGED=1.00 +RUN chordpro --about diff --git a/pp/docker/Makefile b/pp/docker/Makefile new file mode 100644 index 00000000..62d57230 --- /dev/null +++ b/pp/docker/Makefile @@ -0,0 +1,15 @@ +#! /bin/make -f + +DOCKER := docker + +default :: dev + +prod :: + ${DOCKER} build -t chordpro/chordpro:latest . --target chordpro-prod + +dev :: + tar -zcf chordpro-dev.tar.gz -C ../.. -T ../../MANIFEST + ${DOCKER} build -t chordpro/chordpro-dev:latest . --target=chordpro-dev + +clean :: + rm -f qjs abc2svg_qjs.tar.gz chordpro-dev.tar.gz diff --git a/pp/docker/README.md b/pp/docker/README.md index 9bd41bf6..037aa52c 100644 --- a/pp/docker/README.md +++ b/pp/docker/README.md @@ -1,17 +1,24 @@ # Docker -docker build -t chordpro . +## Build -## Normal chordpro + docker build -t chordpro/chordpro:v6.030.0 . + docker tag chordpro/chordpro:v6.030.0 chordpro/chordpro:latest + +## Push to repo + + docker push chordpro/chordpro:latest + +## Running chordpro docker run -ti --rm \ --env HOME=${HOME} \ --env USER=$USER \ --workdir `pwd` \ --volume ${HOME}:${HOME} \ - chordpro chordpro + chordpro/chordpro:latest chordpro -## WxChordPro +## Running wxchordpro docker run --rm \ --env DISPLAY=${DISPLAY} \ @@ -21,7 +28,7 @@ docker build -t chordpro . --env USER=${USER} \ --workdir `pwd` \ --volume ${HOME}:${HOME}:rw \ - chordpro wxchordpro + chordpro/chordpro:latest wxchordpro ## Notes diff --git a/pp/linux/Makefile b/pp/linux/Makefile index 0b5d035c..4d628895 100644 --- a/pp/linux/Makefile +++ b/pp/linux/Makefile @@ -29,7 +29,7 @@ clean :: PERLLIB := /usr/lib64 PERLINC := ${PERLLIB}/perl5/CORE -PERLSO := $(shell perl -wle 'printf("perl%d%06d.so\n",$$1,$$2) if $$] =~ /^(\d+)\.(\d\d\d\d\d\d)/') +PERLSO := $(shell perl -wle 'printf("libperl.so.%d.%d\n",$$1,$$2) if $$] =~ /^(\d+)\.(\d\d\d)/') .PHONY :: ppl ppl :: clean wxchordpro unpack copy_coredll loaders @@ -55,10 +55,13 @@ copy_coredll : cp -pL ${PERLLIB}/libperl.so ${DEST}/${PERLSO} patchelf --set-soname ${PERLSO} ${DEST}/${PERLSO} cp -pL ${PERLLIB}/libpthread.so.0 ${DEST}/ + find . -type f -name '*.so' -exec patchelf --set-rpath '$$ORIGIN' {} \; + find . -type f -name '*.so.*' -exec patchelf --set-rpath '$$ORIGIN' {} \; PREFIX := '"script/"' CCOPTS := $(shell perl -MExtUtils::Embed -e ccopts) -DSCRIPTPREFIX=${PREFIX} LDOPTS := -Wl,--rpath='$${ORIGIN}' ${DEST}/${PERLSO} -lpthread +LDOPTS := -Wl,--rpath='$$ORIGIN' ${DEST}/${PERLSO} -lpthread .PHONY :: loaders loaders : "${DEST}/wxchordpro" "${DEST}/chordpro" diff --git a/pp/linux/wxchordpro.pp b/pp/linux/wxchordpro.pp index 2042b370..e4af2676 100644 --- a/pp/linux/wxchordpro.pp +++ b/pp/linux/wxchordpro.pp @@ -5,20 +5,21 @@ --module=Wx::WebView # Explicitly link the wxGTK3 libraries. ---link=libwx_baseu-3.0.so.0 ---link=libwx_baseu_net-3.0.so.0 ---link=libwx_baseu_xml-3.0.so.0 ---link=libwx_gtk3u_adv-3.0.so.0 ---link=libwx_gtk3u_aui-3.0.so.0 ---link=libwx_gtk3u_core-3.0.so.0 ---link=libwx_gtk3u_html-3.0.so.0 ---link=libwx_gtk3u_media-3.0.so.0 ---link=libwx_gtk3u_propgrid-3.0.so.0 ---link=libwx_gtk3u_ribbon-3.0.so.0 ---link=libwx_gtk3u_richtext-3.0.so.0 +--link=libwx_baseu-3.2.so.0 +--link=libwx_baseu_net-3.2.so.0 +--link=libwx_baseu_xml-3.2.so.0 +--link=libwx_gtk3u_adv-3.2.so.0 +--link=libwx_gtk3u_aui-3.2.so.0 +--link=libwx_gtk3u_core-3.2.so.0 +--link=libwx_gtk3u_html-3.2.so.0 +--link=libwx_gtk3u_media-3.2.so.0 +--link=libwx_gtk3u_propgrid-3.2.so.0 +--link=libwx_gtk3u_ribbon-3.2.so.0 +--link=libwx_gtk3u_richtext-3.2.so.0 # And more... --link=libpng16.so.16 +--link=libjpeg.so.62 --link=libSDL-1.2.so.0 --link=libgconf-2.so.4 --link=libORBit-2.so.0 diff --git a/pp/macos/Makefile b/pp/macos/Makefile index f4c9eea0..59aff3f8 100644 --- a/pp/macos/Makefile +++ b/pp/macos/Makefile @@ -43,8 +43,6 @@ unpack : cp "${DEST}/res/icons/chordpro.icns" ${DEST} cp "${DEST}/res/icons/chordpro-doc.icns" ${DEST} mv "${DEST}/res" "${DEST}/lib/ChordPro/" - tar xf ../common/abc2svg_qjs.tar.gz -C "${DEST}/lib/ChordPro/res/abc" - cp -p qjs "${DEST}/lib/ChordPro/res/abc" rm -f "${DEST}/script/main.pl" # Copy core DLLs. diff --git a/pp/macos/README.html b/pp/macos/README.html index c365f6e7..aefb7c79 100644 --- a/pp/macos/README.html +++ b/pp/macos/README.html @@ -40,14 +40,6 @@

Important notes for running ChordPro on MacOS

Warning!

-

On MacOS 10.9 and above, when a song is loaded, MacOS automatically -changes quotes and apostrophes to typographical quotes. This may cause -problems when you use quoted filenames or option arguments. Also, your -source will be marked as modified, and when you terminate ChordPro you -will be asked to save it. Think twice!

- -

There is currently no way to prevent this.

-

Song files in MS-Windows format (with CR/LF line terminators) may misbehave.

diff --git a/pp/windows/Makefile b/pp/windows/Makefile index 137d3ce3..76468719 100644 --- a/pp/windows/Makefile +++ b/pp/windows/Makefile @@ -33,7 +33,6 @@ clean :: del *.pp.deps #### NOTE: THIS MAY NEED ADJUSTMENT FOR YOUR SYSTEM -ISC := "C:\Program Files (x86)\Inno Setup 6\Compil32.exe" ISCC := "C:\Program Files (x86)\Inno Setup 6\iscc.exe" .PHONY :: iss_par @@ -41,7 +40,7 @@ iss_par :: chordpro.exe wxchordpro.exe innosetup_par.iss chordproinst.bmp copy ${RES}\icons\chordpro.ico . copy ${RES}\icons\chordpro-doc.ico . perl vfix.pl innosetup_par.iss - ${ISC} /cc innosetup_par.iss + ${ISCC} innosetup_par.iss PLIBS := C:\Strawberry\perl\bin CLIBS := C:\Strawberry\c\bin @@ -60,8 +59,6 @@ unpack : copy "${DEST}\res\icons\chordpro.ico" "${DEST}" copy "${DEST}\res\icons\chordpro-doc.ico" "${DEST}" move "${DEST}\res" "${DEST}\lib\ChordPro" - tar xf ..\common\abc2svg_qjs.tar.gz -C "${DEST}\lib\ChordPro\res\abc" - copy qjs.exe "${DEST}\lib\ChordPro\res\abc" # The core DLLs needed for Strawberry perl. The Wx libs have been unpacked already. copy_coredll : @@ -93,7 +90,7 @@ loaders : "${DEST}\wxchordpro.exe" "${DEST}\chordpro.exe" .PHONY :: iss iss : "${DEST}\wxchordpro.exe" innosetup.iss perl vfix.pl - ${ISCC} innosetup.iss + ${ISCC} /q innosetup.iss clean :: -rmdir /q/s "${DEST}" diff --git a/pp/windows/innosetup.iss b/pp/windows/innosetup.iss index d52ad990..52320bad 100644 --- a/pp/windows/innosetup.iss +++ b/pp/windows/innosetup.iss @@ -8,6 +8,8 @@ # define BuildNum 27 [Setup] +ArchitecturesInstallIn64BitMode=x64 arm64 ia64 +ArchitecturesAllowed=x64 arm64 ia64 AppID={{F8D1018C-AAE3-45E6-9447-5997F512F932} AppName={#APP} AppVersion={#V_MAJ}.{#V_MIN}.{#V_AUX}.{#BuildNum}.0 diff --git a/script/chordpro.pl b/script/chordpro.pl index 79f78afe..eadad3b2 100644 --- a/script/chordpro.pl +++ b/script/chordpro.pl @@ -5,8 +5,8 @@ # Author : Johan Vromans # Created On : Fri Jul 9 14:32:34 2010 # Last Modified By: -# Last Modified On: Fri Sep 15 09:39:40 2023 -# Update Count : 271 +# Last Modified On: Fri Nov 3 22:37:19 2023 +# Update Count : 279 # Status : Unknown, Use with caution! ################ Common stuff ################ @@ -36,14 +36,9 @@ =head1 DESCRIPTION use FindBin; use lib "$FindBin::Bin/../lib"; -use lib "$FindBin::Bin/../lib/ChordPro/lib"; -use App::Packager qw( :name ChordPro ); use ChordPro; -use ChordPro::Utils qw(is_msw); - -$ENV{PATH} = join( is_msw() ? ";" : ":", - "$FindBin::Bin", "$FindBin::Bin/..", - $ENV{PATH} ); +use ChordPro::Paths; +CP->pathprepend( "$FindBin::Bin", "$FindBin::Bin/.." ); run(); diff --git a/script/wxchordpro.pl b/script/wxchordpro.pl index 20c0953f..0ae29127 100644 --- a/script/wxchordpro.pl +++ b/script/wxchordpro.pl @@ -13,15 +13,9 @@ package main; use FindBin; use lib "$FindBin::Bin/../lib"; -use lib "$FindBin::Bin/../lib/ChordPro/lib"; -use App::Packager qw( :name ChordPro ); use ChordPro; - -use ChordPro::Utils qw(is_msw); - -$ENV{PATH} = join( is_msw() ? ";" : ":", - "$FindBin::Bin", "$FindBin::Bin/..", - $ENV{PATH} ); +use ChordPro::Paths; +CP->pathprepend( "$FindBin::Bin", "$FindBin::Bin/.." ); # Package name. my $my_package = 'ChordPro'; @@ -33,25 +27,6 @@ package main; # ChordPro::Wx::Main is the main entry of the program. use base qw(Wx::App ChordPro::Wx::Main); -use File::HomeDir; - -$ENV{HOME} //= File::HomeDir->my_home; - -my $app_lc = "chordpro"; -if ( $ENV{XDG_CONFIG_HOME} && -d $ENV{XDG_CONFIG_HOME} ) { - $ENV{CHORDPRO_LIB} ||= File::Spec->catfile( $ENV{XDG_CONFIG_HOME}, $app_lc); -} -elsif ( $ENV{HOME} && -d $ENV{HOME} ) { - my $dir = File::Spec->catfile( $ENV{HOME}, ".config" ); - if ( -d $dir ) { - $ENV{CHORDPRO_LIB} ||= File::Spec->catfile( $dir, $app_lc ); - } - else { - $dir = File::Spec->catfile( $ENV{HOME}, ".$app_lc" ); - $ENV{CHORDPRO_LIB} ||= $dir; - } -} - my $options = app_options(); sub OnInit { diff --git a/t/01_prereq.t b/t/01_prereq.t index afd11b03..300d6573 100644 --- a/t/01_prereq.t +++ b/t/01_prereq.t @@ -20,7 +20,7 @@ my $test; ++$test; ok( $] >= 5.026000, "Perl version $] is 5.026 or newer" ); -++$test; use_ok( "ChordPro::Testing", 6.000 ); +++$test; use_ok( "ChordPro::Testing" ); if ( $pdfapi =~ /^pdf/i ) { ++$test; use_ok( "IO::String", 1.08 ); # for Font::TTF @@ -33,19 +33,18 @@ else { ++$test; use_ok( $pdfapi, $pdfapiv ); diag("Using $pdfapi $pdfapiv for PDF generation"); } -++$test; use_ok( "Text::Layout", 0.031 ); +++$test; use_ok( "Text::Layout", 0.032 ); eval { require HarfBuzz::Shaper; HarfBuzz::Shaper->VERSION(0.026); diag( "Shaping enabled (HarfBuzz::Shaper $HarfBuzz::Shaper::VERSION)" ); 1; } || diag( "Shaping disabled (HarfBuzz::Shaper not found)" ); -++$test; use_ok( "App::Packager", 1.430 ); ++$test; use_ok( "JSON::PP", 2.27203 ); ++$test; use_ok( "String::Interpolate::Named", 1.030 ); ++$test; use_ok( "File::HomeDir", 1.004 ); -++$test; use_ok( "File::LoadLines", 1.021 ); -++$test; use_ok( "SVGPDF", 0.070 ); +++$test; use_ok( "File::LoadLines", 1.042 ); +++$test; use_ok( "SVGPDF", 0.080 ); ++$test; use_ok( "Image::Info", 1.41 ); ++$test; use_ok( "List::Util", 1.33 ); ++$test; use_ok( "Storable", 3.08 ); diff --git a/t/101_directives.t b/t/101_directives.t new file mode 100644 index 00000000..e9b52d74 --- /dev/null +++ b/t/101_directives.t @@ -0,0 +1,122 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use utf8; + +use ChordPro::Testing; +use ChordPro::Songbook; + +my $s = ChordPro::Songbook->new; + +# Just see if all directives are recognized and handled. +my $data = < 1 + 2 * @data; + +eval { $s->parse_file(\$data); 1 } or diag("$@"); +ok( scalar( @{ $s->{songs} } ) == 1, "Directives parsed" ); + +my $song = $s->{songs}->[0]; + +# Add a dummy selector to each directive. +for ( @data ) { + s/^\{(.*?)(\s*:.*)\}$/$1-foo$2/mg + or s/^\{(.*?)\}$/$1-foo/mg; +} + +# Verify that the directives are ignored. +for my $dir ( @data ) { + my $result = $song->parse_directive($dir); + ok( $result->{omit} > 0, "ignored ok: $dir ($result->{name})" ); + p($result) unless $result->{omit} > 0; +} + +$song->{meta}->{foo} = [ 1 ]; +# Verify that the directives are not ignored. +for my $dir ( @data ) { + my $result = $song->parse_directive($dir); + ok( $result->{omit} == 0, "not ignored ok: $dir ($result->{name})" ); + p($result) unless $result->{omit} == 0; +} diff --git a/t/123_memtrans.t b/t/123_memtrans.t new file mode 100644 index 00000000..58a84587 --- /dev/null +++ b/t/123_memtrans.t @@ -0,0 +1,93 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use utf8; + +# Issue 333 + +use ChordPro::Testing; +use ChordPro::Songbook; + +plan tests => 3; + +$config->{settings}->{memorize} = 1; +$config->{settings}->{transpose} = 2; +# Prevent a dummy {body} for chord grids. +$config->{diagrams}->{show} = 0; +my $s = ChordPro::Songbook->new; + +#### meta as meta. + +my $data = <parse_file(\$data) } or diag("$@"); +} +ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); +isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); +# use ChordPro::Dumper; ddp($s->{songs}->[0]); +my $song = { + 'meta' => { + 'songindex' => 1, + 'title' => [ + 'Test Memorize' + ] + }, + 'body' => [ + { + 'type' => 'songline', + 'phrases' => [ + 'This is verse', + ], + 'context' => 'verse', + 'chords' => [ + 'B', + ] + }, + { + 'value' => '', + 'context' => 'verse', + 'name' => 'context', + 'type' => 'set' + }, + { + 'type' => 'songline', + 'phrases' => [ + 'Another verse' + ], + 'context' => 'verse', + 'chords' => [ + 'B', + ] + }, + { + 'value' => '', + 'context' => 'verse', + 'name' => 'context', + 'type' => 'set' + } + ], + 'source' => { + 'line' => 1, + 'file' => '__STRING__' + }, + 'chordsinfo' => { map { $_ => $_ } qw( B ) }, + 'title' => 'Test Memorize', + 'system' => 'common', + 'structure' => 'linear', + 'settings' => {}, + }; + +is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); + diff --git a/t/124_memtrans.t b/t/124_memtrans.t new file mode 100644 index 00000000..c8f3473e --- /dev/null +++ b/t/124_memtrans.t @@ -0,0 +1,94 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use utf8; + +# Issue 333 + +use ChordPro::Testing; +use ChordPro::Songbook; + +plan tests => 3; + +$config->{settings}->{memorize} = 1; +$config->{settings}->{transpose} = 2; +# Prevent a dummy {body} for chord grids. +$config->{diagrams}->{show} = 0; +my $s = ChordPro::Songbook->new; + +#### meta as meta. + +my $data = <parse_file(\$data) } or diag("$@"); +} +ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); +isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); +# use ChordPro::Dumper; ddp($s->{songs}->[0]); +my $song = { + 'meta' => { + 'songindex' => 1, + 'title' => [ + 'Test Memorize' + ] + }, + 'body' => [ + { + 'type' => 'songline', + 'phrases' => [ + 'This is verse', + ], + 'context' => 'verse', + 'chords' => [ + 'B', + ] + }, + { + 'value' => '', + 'context' => 'verse', + 'name' => 'context', + 'type' => 'set' + }, + { + 'type' => 'songline', + 'phrases' => [ + 'Another verse' + ], + 'context' => 'verse', + 'chords' => [ + 'C', + ] + }, + { + 'value' => '', + 'context' => 'verse', + 'name' => 'context', + 'type' => 'set' + } + ], + 'source' => { + 'line' => 1, + 'file' => '__STRING__' + }, + 'chordsinfo' => { map { $_ => $_ } qw( B C ) }, + 'title' => 'Test Memorize', + 'system' => 'common', + 'structure' => 'linear', + 'settings' => {}, + }; + +is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); + diff --git a/t/130_image.t b/t/130_image.t index 5042a4fd..42fd6321 100644 --- a/t/130_image.t +++ b/t/130_image.t @@ -25,6 +25,12 @@ ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { + assets => { + _Image001 => { + type => 'image', + uri => '130_image.jpg' + } + }, 'settings' => {}, 'meta' => { 'songindex' => 1, @@ -36,7 +42,7 @@ my $song = { 'body' => [ { 'context' => '', - 'uri' => './130_image.jpg', + 'id' => '_Image001', 'type' => 'image', 'opts' => {} } @@ -59,7 +65,7 @@ eval { $s->parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); -#use Data::Dumper; warn(Dumper($s)); +#use DDP; p($s->{songs}[0]); $song = { 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, @@ -71,6 +77,12 @@ $song = { 'Swing Low Sweet Chariot' ] }, + assets => { + _Image002 => { + type => 'image', + uri => '130_image.jpg' + } + }, 'settings' => {}, 'body' => [ { @@ -80,10 +92,10 @@ $song = { 'height' => '150', 'border' => '2', 'scale' => '4', - 'center' => 1, + 'align' => 'center', 'width' => '200' }, - 'uri' => './130_image.jpg', + 'id' => '_Image002', 'context' => '' } ], diff --git a/t/131_image.t b/t/131_image.t index a48a87d5..87976ced 100644 --- a/t/131_image.t +++ b/t/131_image.t @@ -27,7 +27,7 @@ eval { $s->parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); -#use Data::Dumper; warn(Dumper($s)); +#use DDP; p $s->{songs}->[0]; delete( $s->{songs}->[0]->{assets}->{white}->{data} ) if $s->{songs}->[0]->{assets}->{white}->{data} =~ /^\xff\xd8\xff\xe0/; @@ -43,16 +43,24 @@ my $song = { 'body' => [ { 'context' => '', - 'uri' => 'id=white', + 'id' => 'white', 'type' => 'image', 'opts' => {} } ], 'assets' => { white => { - type => 'jpg', + type => 'image', + subtype => 'jpg', width => 1, height => 1, + opts => { + enc => 'base64', + height => 1, + id => 'white', + type => 'jpg', + width => 1 + }, }, }, 'source' => { file => "__STRING__", line => 1 }, diff --git a/t/132_image.t b/t/132_image.t new file mode 100644 index 00000000..df015237 --- /dev/null +++ b/t/132_image.t @@ -0,0 +1,227 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use utf8; + +use ChordPro::Testing; +use ChordPro::Songbook; + +plan tests => 3; + +# Prevent a dummy {body} for chord grids. +$config->{diagrams}->{show} = 0; +my $s = ChordPro::Songbook->new; + +# Image (minimal). +my $data = < + + +{end_of_svg} + +{image id=green} +{c Green box above (centered)} + +{start_of_svg} +center=0 + + + +{end_of_svg} +{c Blue box above (centered)} + +##image: id=yellow persist=1 +# /9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkI +# CQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQ +# EBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAyADIDAREA +# AhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAn/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFgEB +# AQEAAAAAAAAAAAAAAAAAAAYJ/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8AoKyq +# XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//2Q== +##asset: id=red type=svg persist=1 +# +# +# +EOD + +eval { $s->parse_file(\$data) } or diag("$@"); + +ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); +isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); +#use DDP; p $s->{songs}->[0]; +delete( $s->{songs}->[0]->{assets}->{yellow}->{data} ) + if $s->{songs}->[0]->{assets}->{yellow}->{data} =~ /^\xff\xd8\xff\xe0/; + +my $song = { + 'assets' => { + 'yellow' => { + 'height' => 50, + 'width' => 50, + 'type' => 'image', + 'subtype' => 'jpg', + opts => { id => 'yellow', + 'persist' => 1 } + }, + 'red' => { + 'data' => [ + '', + '', + '' + ], + 'handler' => 'svg2svg', + 'type' => 'image', + 'subtype' => 'svg', + opts => { id => 'red', + type => 'svg', + 'persist' => 1 }, + 'module' => 'SVG' + }, + 'green' => { + 'delegate' => 'SVG', + 'data' => [ + '', + '', + '' + ], + 'opts' => { + 'id' => 'green', + 'center' => '0' + }, + 'type' => 'image', + 'handler' => 'svg2svg', + 'subtype' => 'delegate' + }, + '_Image001' => { + 'handler' => 'svg2svg', + 'type' => 'image', + 'subtype' => 'delegate', + 'delegate' => 'SVG', + 'data' => [ + '', + '', + '' + ], + 'opts' => { + 'center' => '0' + } + } + }, + 'meta' => { + 'songindex' => 1, + 'chords' => [], + 'numchords' => [ + 0 + ], + 'title' => [ + 'Testing images' + ] + }, + 'settings' => {}, + 'title' => 'Testing images', + 'chordsinfo' => {}, + 'source' => { + 'file' => '__STRING__', + 'line' => 1 + }, + 'body' => [ + { + 'context' => '', + 'type' => 'empty' + }, + { + 'context' => '', + 'opts' => { + 'border' => '1', + 'anchor' => 'line', + 'scale' => '0.5', + 'x' => '100%' + }, + 'id' => 'yellow', + 'type' => 'image' + }, + { + 'id' => 'yellow', + 'type' => 'image', + 'opts' => { + 'border' => '1', + 'scale' => '0.5', + 'anchor' => 'line', + 'x' => '-30' + }, + 'context' => '' + }, + { + 'orig' => 'This line has two image hint', + 'text' => 'This line has two image hint', + 'type' => 'comment', + 'context' => '' + }, + { + 'type' => 'empty', + 'context' => '' + }, + { + 'value' => '', + 'name' => 'context', + 'type' => 'set', + 'context' => 'svg' + }, + { + 'context' => '', + 'type' => 'empty' + }, + { + 'opts' => {}, + 'id' => 'green', + 'type' => 'image', + 'context' => '' + }, + { + 'text' => 'Green box above (centered)', + 'type' => 'comment', + 'context' => '', + 'orig' => 'Green box above (centered)' + }, + { + 'type' => 'empty', + 'context' => '' + }, + { + 'context' => 'svg', + 'type' => 'image', + 'id' => '_Image001', + 'opts' => { + 'center' => '0' + } + }, + { + 'context' => 'svg', + 'type' => 'set', + 'name' => 'context', + 'value' => '' + }, + { + 'orig' => 'Blue box above (centered)', + 'context' => '', + 'type' => 'comment', + 'text' => 'Blue box above (centered)' + }, + { + 'type' => 'empty', + 'context' => '' + } + ], + 'structure' => 'linear', + 'system' => 'common' + }; + +is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); diff --git a/t/174_transpose.t b/t/174_transpose.t index b5878625..71e90b67 100644 --- a/t/174_transpose.t +++ b/t/174_transpose.t @@ -4,7 +4,6 @@ use strict; use warnings; use utf8; -use App::Packager qw( :name ChordPro ); use ChordPro; use Test::More; diff --git a/t/190_outlines.t b/t/190_outlines.t index 80e0da50..597726a5 100644 --- a/t/190_outlines.t +++ b/t/190_outlines.t @@ -286,4 +286,7 @@ my $xp = [ ], ]; +foreach ( @$res ) { + delete $_->[2]->{meta}->{_configversion}; +} is_deeply( $res, $xp, "outlined"); diff --git a/t/191_outlines.t b/t/191_outlines.t index 2ac649ee..982b2ccc 100644 --- a/t/191_outlines.t +++ b/t/191_outlines.t @@ -283,4 +283,7 @@ my $xp = [ ], ]; +foreach ( @$res ) { + delete $_->[2]->{meta}->{_configversion}; +} is_deeply( $res, $xp, "outlined"); diff --git a/t/192_outlines.t b/t/192_outlines.t index 628ce3d5..f8b6155e 100644 --- a/t/192_outlines.t +++ b/t/192_outlines.t @@ -286,4 +286,7 @@ my $xp = [ ], ]; +foreach ( @$res ) { + delete $_->[2]->{meta}->{_configversion}; +} is_deeply( $res, $xp, "outlined"); diff --git a/t/193_outlines.t b/t/193_outlines.t index fb716019..c138e201 100644 --- a/t/193_outlines.t +++ b/t/193_outlines.t @@ -175,4 +175,7 @@ my $xp = [ ], ]; +foreach ( @$res ) { + delete $_->[1]->{meta}->{_configversion}; +} is_deeply( $res, $xp, "outlined"); diff --git a/t/194_outlines.t b/t/194_outlines.t index 5da2820e..d8772bc6 100644 --- a/t/194_outlines.t +++ b/t/194_outlines.t @@ -176,4 +176,7 @@ my $xp = [ ], ]; +foreach ( @$res ) { + delete $_->[1]->{meta}->{_configversion}; +} is_deeply( $res, $xp, "outlined"); diff --git a/t/50_encodings.t b/t/50_encodings.t index ec8d0ff3..80e9b622 100644 --- a/t/50_encodings.t +++ b/t/50_encodings.t @@ -57,11 +57,25 @@ my %enc2bom = map { $_ => encode($_, "\x{feff}") } @BOMs; enctest( $_, 1 ) for @noBOMs; enctest($_) for @BOMs; -done_testing( 3 * ( 1 + @noBOMs + @BOMs ) ); +done_testing( 3 * ( 1 + 4*(@noBOMs + @BOMs) ) ); sub enctest { my ( $enc, $nobom ) = @_; my $encoded = $data; + _enctest( $encoded, $enc, $nobom ); + $encoded = $data; + $encoded =~ s/\n/\x0a/g; + _enctest( $encoded, $enc, $nobom, "LF" ); + $encoded = $data; + $encoded =~ s/\n/\x0d/g; + _enctest( $encoded, $enc, $nobom, "CR" ); + $encoded = $data; + $encoded =~ s/\n/\x0d\x0a/g; + _enctest( $encoded, $enc, $nobom, "CRLF" ); +} + +sub _enctest { + my ( $encoded, $enc, $nobom, $crlf ) = @_; from_to( $encoded, "UTF-8", $enc ); unless ( $nobom ) { BAIL_OUT("Unknown encoding: $enc") unless $enc2bom{$enc}; @@ -69,11 +83,11 @@ sub enctest { } my $fn = "out/$enc.cho"; - open( my $fh, ">", $fn ) or die("$fn: $!\n"); + open( my $fh, ">:raw", $fn ) or die("$fn: $!\n"); print $fh $encoded; close($fh); - $enc .= " (no BOM)" if $nobom; + $enc .= " ($crlf)" if $crlf; my $s = ChordPro::Songbook->new; eval { $s->parse_file($fn) } or diag("$@"); diff --git a/t/a2crd/t105.cho b/t/a2crd/t105.cho index 8563d566..1f566cbb 100644 --- a/t/a2crd/t105.cho +++ b/t/a2crd/t105.cho @@ -12,3 +12,5 @@ D|--3-----3------------------------2--------------1-------| A|------------------------------------------------1-------| E|------------------------------------------------0-------| {eot} + +ttt ttt tttttt ttt [B]ttt[Gm]tt [C] ttt ttt [F]ttt tt [Dsus4]tttt[D] diff --git a/t/md/cho004.md b/t/md/cho004.md index 056e6706..01d19d75 100644 --- a/t/md/cho004.md +++ b/t/md/cho004.md @@ -9,13 +9,10 @@ I looked over Jordan, and what did I see, Gb B Gb I looked over Jordan, and what did I see, - G# C# G# I looked over Jordan, and what did I see, - F# B F# I looked over Jordan, and what did I see, - D G D I looked over Jordan, and what did I see, diff --git a/xt/01_config.t b/xt/01_config.t new file mode 100644 index 00000000..51732112 --- /dev/null +++ b/xt/01_config.t @@ -0,0 +1,29 @@ +#! perl + +use strict; +use warnings; +use utf8; + +# Checking consistency between the JSON and PRP versions of the config. + +use ChordPro::Testing; +use ChordPro::Config::Properties; +use JSON::PP; +use File::LoadLines qw(loadblob); + +my $lib = "lib/ChordPro/res/config"; +$lib = "../$lib" unless -d 'xt'; + +my $dp = Data::Properties->new; +my $d1 = $dp->parse_file("$lib/chordpro.prp")->data; +ok( $d1, "Got PRP config"); +#diag("PRP: ", scalar(keys(%$d1)), " top level keys"); + +my $d2 = JSON::PP->new->relaxed->utf8(1) + ->boolean_values(0,1) + ->decode(loadblob("$lib/chordpro.json")); +ok( $d2, "Got JSON config"); +#diag("JSON: ", scalar(keys(%$d2)), " top level keys"); + +Test::More::is_deeply( $d1, $d2, "PRP and JSON config match" ); +