abc2svg
Check-in Differences
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Difference From 1f1e9ecb656a9e71 To 93aa3a8ca3fc968e

2024-12-15
18:10
modules: strtab: fix: lack of stem on grace notes ... (check-in: 6a540b1c49 user: jef tags: trunk)
2024-12-14
18:07
New release v1.22.23 ... (check-in: 93aa3a8ca3 user: jef tags: trunk, v1.22.23)
07:41
core: add %%partname ... (check-in: bc88c24d2f user: jef tags: trunk)
2024-02-01
10:50
core: keep the multi-measure rests of the second voices of the staves - asked by Gerhard Schacherl ... (check-in: fc7af4436c user: jef tags: trunk)
2024-01-30
16:56
New release v1.22.13 ... (check-in: 1f1e9ecb65 user: jef tags: trunk, v1.22.13)
2024-01-27
15:05
core: better vertical offset of rests when many voices ... (check-in: e8149de154 user: jef tags: trunk)

Changes to README.md.
︙︙
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171

It puts the lines of music on vertical areas.  
Without this style, the music is in-lined as in [this other example][5].

[5]: http://moinejf.free.fr/abcm2ps-doc/dansou-i.html "Tune index example"

Global ABC parameters may be added as `parameter=value` in the query string
of the URL of the page. The [following example][16] calls the same
"J'ai du bon tabac" with `pagescale=1.2` (giving `%%pagescale 1.2`).

[16]: http://moinejf.free.fr/abcm2ps-doc/tabac.html?pagescale=1.2 "J'ai du bon tabac"

In the above examples, all the ABC music is generated (displayed
and ready to be played) by means of the script **abcweb-1.js**.  
If there is a large collection of tunes, it may be preferable to
link to the other script, **abcweb1-1.js**, which offers a tune selection.
Without an explicit
selection (see below), a list of the tunes is displayed, and,







|


|







154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171

It puts the lines of music on vertical areas.  
Without this style, the music is in-lined as in [this other example][5].

[5]: http://moinejf.free.fr/abcm2ps-doc/dansou-i.html "Tune index example"

Global ABC parameters may be added as `parameter=value` in the query string
of the URL of the page. The [following example][9] calls the same
"J'ai du bon tabac" with `pagescale=1.2` (giving `%%pagescale 1.2`).

[9]: http://moinejf.free.fr/abcm2ps-doc/tabac.html?pagescale=1.2 "J'ai du bon tabac"

In the above examples, all the ABC music is generated (displayed
and ready to be played) by means of the script **abcweb-1.js**.  
If there is a large collection of tunes, it may be preferable to
link to the other script, **abcweb1-1.js**, which offers a tune selection.
Without an explicit
selection (see below), a list of the tunes is displayed, and,
︙︙
345
346
347
348
349
350
351






352
353
354
355
356
357
358
  after `-o` (default `abc.odt`).  
  Example:  
  `        abcqjs toodt.js my_file.abc -o my_file.odt`

- `toparam.js`  
  This script just outputs the abc2svg parameters.







#### 2.3 PDF generation

`abctopdf` is a shell script which converts ABC to PDF using one of the
previous shell scripts and, either a chrome/chromium compatible web browser
(settable by the environment variable 'BROWSER'),
or the program [weasyprint](https://weasyprint.org/).








>
>
>
>
>
>







345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
  after `-o` (default `abc.odt`).  
  Example:  
  `        abcqjs toodt.js my_file.abc -o my_file.odt`

- `toparam.js`  
  This script just outputs the abc2svg parameters.

- `tosvg.js`  
  This script creates a SVG file that includes all the SVGs of the generation.

    The included SVG images are updated for being correctly rendered by tools
  that use librsvg, the SVG library for GNOME (feh, viewnior, swayimg...).

#### 2.3 PDF generation

`abctopdf` is a shell script which converts ABC to PDF using one of the
previous shell scripts and, either a chrome/chromium compatible web browser
(settable by the environment variable 'BROWSER'),
or the program [weasyprint](https://weasyprint.org/).

︙︙
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
  Playing and highlighting the played notes may be offered loading
  the script `snd-1.js` (see below).

[15]: http://moinejf.free.fr/abcm2ps-doc/inline.html "abc2svg - inline music"

    This script also accepts a parameter `with_source`.
    When this parameter is set, the music source is included before the
    SVG images of the music. An argument `nohead` prevents displaying
    the source of the first music sequence. The music source is displayed
    in a <pre> element of class `source`. The SVG's are included
    in a <div> of the same class `source`.
    The source may be displayed either above (default) or
    at the left side of the music (using a style as
    `.source{display: inline-block; vertical-align: top}`).
    See the source of
    [abcm2ps/abc2svg features](http://moinejf.free.fr/abcm2ps-doc/features.html)
    for an example.

    The music source may be editable.
    To change it, the script contains two functions:  
    - `abc2svg.get_music` returns the source of the music sequence
       (in <script> type "text/vnd.abc", class="abc" or inlined ABC)
       Its argument is the HTML <div> element that contains the music.  
    - `abc2svg.set_music` has two arguments, the HTML <div> element
       and the new source of the music.
       It generates and replaces the music in the <div>.

- `abcweb1-1.js`  
  The page body is analyzed as a ABC file and its content is







<
|












|







503
504
505
506
507
508
509

510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
  Playing and highlighting the played notes may be offered loading
  the script `snd-1.js` (see below).

[15]: http://moinejf.free.fr/abcm2ps-doc/inline.html "abc2svg - inline music"

    This script also accepts a parameter `with_source`.
    When this parameter is set, the music source is included before the

    SVG images of the music. The music source is displayed
    in a &lt;pre&gt; element of class `source`. The SVG's are included
    in a &lt;div&gt; of the same class `source`.
    The source may be displayed either above (default) or
    at the left side of the music (using a style as
    `.source{display: inline-block; vertical-align: top}`).
    See the source of
    [abcm2ps/abc2svg features](http://moinejf.free.fr/abcm2ps-doc/features.html)
    for an example.

    The music source may be editable.
    To change it, the script contains two functions:  
    - `abc2svg.get_music` returns the source of the music sequence
       (in &lt;script&gt; type "text/vnd.abc", class="abc" or inlined ABC).
       Its argument is the HTML &lt;div&gt; element that contains the music.  
    - `abc2svg.set_music` has two arguments, the HTML &lt;div&gt; element
       and the new source of the music.
       It generates and replaces the music in the &lt;div&gt;.

- `abcweb1-1.js`  
  The page body is analyzed as a ABC file and its content is
︙︙
Changes to abc2svg.ttf.

cannot compute difference between binary files

Changes to abcqjs.
1
2
3
4
5
6
7
8
:;# shell script to convert ABC or MEI music notation to other format
# using abc2svg and quickjs (https://bellard.org/quickjs/)

# search where are the abc2svg scripts
p=$0
if [ `dirname $p` = . ]; then
	p=`which $p`
fi
|







1
2
3
4
5
6
7
8
# shell script to convert ABC or MEI music notation to other format
# using abc2svg and quickjs (https://bellard.org/quickjs/)

# search where are the abc2svg scripts
p=$0
if [ `dirname $p` = . ]; then
	p=`which $p`
fi
︙︙
66
67
68
69
70
71
72




73
74
75
76
77
		}
	}

	load(path + core)
	load(path + "cmdline.js")

	abc_cmd("abcqjs", args, "QuickJS")




' $p "$@"

The previous solution with the shebang line as
#!/usr/bin/env -S qjs --std
did not work with non-GNU versions of env







>
>
>
>





66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
		}
	}

	load(path + core)
	load(path + "cmdline.js")

	abc_cmd("abcqjs", args, "QuickJS")

// test: list the global variables
//for (var k in this)
// console.log(k)
' $p "$@"

The previous solution with the shebang line as
#!/usr/bin/env -S qjs --std
did not work with non-GNU versions of env
Changes to abctopdf.
︙︙
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
		set -- "$@" "$a"
	fi
	shift
done

# check if some chromium-based browser is available
for c in "$BROWSER" chromium-browser vivaldi opera brave-browser epic google-chrome; do
	if command -v "$c" >/dev/null 2>&1; then
		echo "and $c"
		command $abcscr "$@" > /tmp/o.html
# --headless=new does not work
		"$c" --headless --print-to-pdf=$out --no-pdf-header-footer\
			/tmp/o.html
		rm /tmp/o.html
		exit 0
	fi
done

# try weasyprint







|


|
|







83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
		set -- "$@" "$a"
	fi
	shift
done

# check if some chromium-based browser is available
for c in "$BROWSER" chromium-browser vivaldi opera brave-browser epic google-chrome; do
	if command -v $c >/dev/null 2>&1; then
		echo "and $c"
		command $abcscr "$@" > /tmp/o.html
# --headless does not work anymore (since vivaldi 6.9 2024-09-10)
		$c --headless=old --print-to-pdf=$out --no-pdf-header-footer\
			/tmp/o.html
		rm /tmp/o.html
		exit 0
	fi
done

# try weasyprint
︙︙
Changes to build.
︙︙
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# no minify
gen() {
 cat "$@"
}
fi

out=abc2svg-$MAJOR.js
VERSION='??????'
DATE=`date -Idate`
cp core/license $out
gen core/abc2svg.js core/deco.js core/draw.js\
 font.js core/format.js core/front.js core/music.js core/parse.js core/subs.js\
 core/svg.js core/tune.js core/lyrics.js core/gchord.js core/tail.js\
 core/modules.js >> $out
 echo "abc2svg.version=\"$VERSION\";abc2svg.vdate=\"$DATE\"" >> $out







|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# no minify
gen() {
 cat "$@"
}
fi

out=abc2svg-$MAJOR.js
VERSION=`sed -e 's/^[^"]*"//' -e's/".*//' version.txt`
DATE=`date -Idate`
cp core/license $out
gen core/abc2svg.js core/deco.js core/draw.js\
 font.js core/format.js core/front.js core/music.js core/parse.js core/subs.js\
 core/svg.js core/tune.js core/lyrics.js core/gchord.js core/tail.js\
 core/modules.js >> $out
 echo "abc2svg.version=\"$VERSION\";abc2svg.vdate=\"$DATE\"" >> $out
︙︙
Changes to build.ninja.
︙︙
22
23
24
25
26
27
28
29
30
31

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

50
51
52
53
54
55
56

rule jsc
  command = if [ -e .fslckout ] && command -v fossil >/dev/null 2>&1; then $
	v=`fossil describe`;$
	d=`date -Idate`;$
	v="abc2svg.version=\"$$v\";abc2svg.vdate=\"$$d\"";$
   else $
	v=`grep abc2svg.version $out`;$
   fi;$
   cp core/license $out;$

   if [ "x$$NOMIN" = "x" ]; then $
      if command -v jsmin >/dev/null 2>&1; then $
	cat $in | jsmin >> $out;$
	echo "" >> $out;$
      else $
	if command -v qjs >/dev/null 2>&1; then $
	  cat $in | ./jsmin >> $out;$
	else $
	  cat $in | uglifyjs >> $out;$
	fi;$
     fi;$
   else $
	cat $in >> $out;$
   fi;$
   echo $$v >> $out

rule jscu
  command = cp core/license $out;$

   if [ "x$$NOMIN" = "x" ]; then $
      if command -v jsmin >/dev/null 2>&1; then $
	cat $in | jsmin >> $out;$
	echo "" >> $out;$
      else $
	if command -v qjs >/dev/null 2>&1; then $
	  cat $in | ./jsmin >> $out;$







|


>


















>







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

rule jsc
  command = if [ -e .fslckout ] && command -v fossil >/dev/null 2>&1; then $
	v=`fossil describe`;$
	d=`date -Idate`;$
	v="abc2svg.version=\"$$v\";abc2svg.vdate=\"$$d\"";$
   else $
	v=`cat version.txt`;$
   fi;$
   cp core/license $out;$
   head -qn1 $in >> $out;$
   if [ "x$$NOMIN" = "x" ]; then $
      if command -v jsmin >/dev/null 2>&1; then $
	cat $in | jsmin >> $out;$
	echo "" >> $out;$
      else $
	if command -v qjs >/dev/null 2>&1; then $
	  cat $in | ./jsmin >> $out;$
	else $
	  cat $in | uglifyjs >> $out;$
	fi;$
     fi;$
   else $
	cat $in >> $out;$
   fi;$
   echo $$v >> $out

rule jscu
  command = cp core/license $out;$
   head -qn1 $in >> $out;$
   if [ "x$$NOMIN" = "x" ]; then $
      if command -v jsmin >/dev/null 2>&1; then $
	cat $in | jsmin >> $out;$
	echo "" >> $out;$
      else $
	if command -v qjs >/dev/null 2>&1; then $
	  cat $in | ./jsmin >> $out;$
︙︙
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
build font: font | font/abc2svg.sfd
build sf: sf | Scc1t2/

build abc2svg-$MAJOR.js: jsc core/abc2svg.js core/deco.js core/draw.js $
 font.js core/format.js core/front.js core/music.js core/parse.js core/subs.js $
 core/svg.js core/tune.js core/lyrics.js core/gchord.js core/tail.js $
 core/modules.js
#| package.json

build ambitus-$MAJOR.js: jscu modules/ambitus.js
build break-$MAJOR.js: jscu modules/break.js
build capo-$MAJOR.js: jscu modules/capo.js
build chordnames-$MAJOR.js: jscu modules/chordnames.js
build clair-$MAJOR.js: jscu modules/clair.js
build clip-$MAJOR.js: jscu modules/clip.js







<







92
93
94
95
96
97
98

99
100
101
102
103
104
105
build font: font | font/abc2svg.sfd
build sf: sf | Scc1t2/

build abc2svg-$MAJOR.js: jsc core/abc2svg.js core/deco.js core/draw.js $
 font.js core/format.js core/front.js core/music.js core/parse.js core/subs.js $
 core/svg.js core/tune.js core/lyrics.js core/gchord.js core/tail.js $
 core/modules.js


build ambitus-$MAJOR.js: jscu modules/ambitus.js
build break-$MAJOR.js: jscu modules/break.js
build capo-$MAJOR.js: jscu modules/capo.js
build chordnames-$MAJOR.js: jscu modules/chordnames.js
build clair-$MAJOR.js: jscu modules/clair.js
build clip-$MAJOR.js: jscu modules/clip.js
︙︙
119
120
121
122
123
124
125

126
127
128
129
130
131
132
build perc-$MAJOR.js: jscu modules/perc.js
build psvg-$MAJOR.js: jscu modules/wps.js modules/psvg.js
build roman-$MAJOR.js: jscu modules/roman.js
build soloffs-$MAJOR.js: jscu modules/soloffs.js
build sth-$MAJOR.js: jscu modules/sth.js
build strtab-$MAJOR.js: jscu modules/strtab.js
build temper-$MAJOR.js: jscu modules/temper.js

build tropt-$MAJOR.js: jscu modules/tropt.js

build edit-$MAJOR.css: cp editor/edit.css

build abcdoc-$MAJOR.js: jscu util/abcdoc.js
build abcweb-$MAJOR.js: jscu util/abcweb.js
build abcweb1-$MAJOR.js: jscu util/abcweb1.js







>







120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
build perc-$MAJOR.js: jscu modules/perc.js
build psvg-$MAJOR.js: jscu modules/wps.js modules/psvg.js
build roman-$MAJOR.js: jscu modules/roman.js
build soloffs-$MAJOR.js: jscu modules/soloffs.js
build sth-$MAJOR.js: jscu modules/sth.js
build strtab-$MAJOR.js: jscu modules/strtab.js
build temper-$MAJOR.js: jscu modules/temper.js
build tempo-$MAJOR.js: jscu modules/tempo.js
build tropt-$MAJOR.js: jscu modules/tropt.js

build edit-$MAJOR.css: cp editor/edit.css

build abcdoc-$MAJOR.js: jscu util/abcdoc.js
build abcweb-$MAJOR.js: jscu util/abcweb.js
build abcweb1-$MAJOR.js: jscu util/abcweb1.js
︙︙
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169


170
171
172
173
174
175
176
177
178
179
180
181
182
183
 ambitus-$MAJOR.js break-$MAJOR.js capo-$MAJOR.js $
 chordnames-$MAJOR.js clair-$MAJOR.js clip-$MAJOR.js $
 combine-$MAJOR.js diag-$MAJOR.js equalbars-$MAJOR.js gamelan-$MAJOR.js $
 grid-$MAJOR.js grid2-$MAJOR.js grid3-$MAJOR.js jianpu-$MAJOR.js $
 jazzchord-$MAJOR.js  mdnn-$MAJOR.js MIDI-$MAJOR.js $
 nns-$MAJOR.js page-$MAJOR.js pedline-$MAJOR.js perc-$MAJOR.js psvg-$MAJOR.js $
 roman-$MAJOR.js soloffs-$MAJOR.js sth-$MAJOR.js strtab-$MAJOR.js $
 temper-$MAJOR.js tropt-$MAJOR.js edit-$MAJOR.css edit-$MAJOR.js edit-$MAJOR.xhtml $
 abckbd-$MAJOR.js abckbd2-$MAJOR.js

default all

# fossil new release
rule version
  command = v=`fossil changes|wc -l`;$
	if [ $$v -ne 0 ]; then $
		echo "!! There are changes !!";$
		exit 1;$
	fi;$
	v=`fossil describe`;$
	if [ "$$out"="patch" ]; then $
		v=`echo $$v|awk -F. -v OFS=. '{$$3 += 1; print}'`;$
	else $
		v=`echo $$v|awk -F. -v OFS=. '{$$2 += 1; $$3=0; print}'`;$
	fi;$


	fossil commit --allow-empty -m "New release $$v" -tag $$v;$
	n=`tail -1 abc2svg-1.js|wc -c`;$
	truncate -s -$$n abc2svg-1.js;$
	d=`date -Idate`;$
	echo "abc2svg.version=\"$$v\";abc2svg.vdate=\"$$d\"" >> abc2svg-1.js;$
	r="abc2svg-$$v";$
	ln -s . $$r;$
	sed "s#^#$$r/#" dist_bin | pax -wj -x tar -f abc2svg.tar.bz2;$
	rm -f abc2svg.zip;$
	sed "s#^#$$r/#" dist_bin | zip -r abc2svg.zip -@;$
	rm $$r;$
	echo "Don't forget to upload abc2svg.tar.bz2 and abc2svg.zip"
build minor: version
build patch: version







|
|
















>
>
|


<
|









147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176

177
178
179
180
181
182
183
184
185
186
 ambitus-$MAJOR.js break-$MAJOR.js capo-$MAJOR.js $
 chordnames-$MAJOR.js clair-$MAJOR.js clip-$MAJOR.js $
 combine-$MAJOR.js diag-$MAJOR.js equalbars-$MAJOR.js gamelan-$MAJOR.js $
 grid-$MAJOR.js grid2-$MAJOR.js grid3-$MAJOR.js jianpu-$MAJOR.js $
 jazzchord-$MAJOR.js  mdnn-$MAJOR.js MIDI-$MAJOR.js $
 nns-$MAJOR.js page-$MAJOR.js pedline-$MAJOR.js perc-$MAJOR.js psvg-$MAJOR.js $
 roman-$MAJOR.js soloffs-$MAJOR.js sth-$MAJOR.js strtab-$MAJOR.js $
 temper-$MAJOR.js tempo-$MAJOR.js tropt-$MAJOR.js edit-$MAJOR.css edit-$MAJOR.js $
 edit-$MAJOR.xhtml abckbd-$MAJOR.js abckbd2-$MAJOR.js

default all

# fossil new release
rule version
  command = v=`fossil changes|wc -l`;$
	if [ $$v -ne 0 ]; then $
		echo "!! There are changes !!";$
		exit 1;$
	fi;$
	v=`fossil describe`;$
	if [ "$$out"="patch" ]; then $
		v=`echo $$v|awk -F. -v OFS=. '{$$3 += 1; print}'`;$
	else $
		v=`echo $$v|awk -F. -v OFS=. '{$$2 += 1; $$3=0; print}'`;$
	fi;$
	d=`date -Idate`;$
	echo "abc2svg.version=\"$$v\";abc2svg.vdate=\"$$d\"" > version.txt;$
	fossil commit -m "New release $$v" -tag $$v;$
	n=`tail -1 abc2svg-1.js|wc -c`;$
	truncate -s -$$n abc2svg-1.js;$

	cat version.txt >> abc2svg-1.js;$
	r="abc2svg-$$v";$
	ln -s . $$r;$
	sed "s#^#$$r/#" dist_bin | pax -wj -x tar -f abc2svg.tar.bz2;$
	rm -f abc2svg.zip;$
	sed "s#^#$$r/#" dist_bin | zip -r abc2svg.zip -@;$
	rm $$r;$
	echo "Don't forget to upload abc2svg.tar.bz2 and abc2svg.zip"
build minor: version
build patch: version
Changes to cmdline.js.
︙︙
92
93
94
95
96
97
98





99
100
101
102
103
104
105

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







>
>
>
>
>







92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

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)
		if (abc) {
			abc.parse.state = 0		// force block flush
			abc.blk_flush()
			abc2svg.abc_end()
		}
		abc2svg.quit()
	} // abort()

	// put the last options before the last file
	function arg_reorder(a) {
	    var	f,
		i = a.length - 2
︙︙
Changes to core/abc2svg.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - abc2svg.js
//
// Copyright (C) 2014-2022 Jean-Francois Moine
//
// This file is part of abc2svg-core.
//
// abc2svg-core is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.


|







1
2
3
4
5
6
7
8
9
10
// abc2svg - abc2svg.js
//
// Copyright (C) 2014-2024 Jean-Francois Moine
//
// This file is part of abc2svg-core.
//
// abc2svg-core is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
︙︙
167
168
169
170
171
172
173



174
175
176
177
178
179
180
	"sus": "sus4",
	"7sus": "7sus4"
} // ch_alias

// global fonts
abc2svg.font_tb = []	// fonts - index = font.fid
abc2svg.font_st = {}	// font style => font_tb index for incomplete user fonts




// font weight
// reference:
//	https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight
abc2svg.ft_w = {
	thin: 100,
	extralight: 200,







>
>
>







167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
	"sus": "sus4",
	"7sus": "7sus4"
} // ch_alias

// global fonts
abc2svg.font_tb = []	// fonts - index = font.fid
abc2svg.font_st = {}	// font style => font_tb index for incomplete user fonts

// cache for converting a duration into [head, dots, nflags]
abc2svg.hdn = {}

// font weight
// reference:
//	https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight
abc2svg.ft_w = {
	thin: 100,
	extralight: 200,
︙︙
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
	}
	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)		// only one error message per symbol
			return
		s.err = true
	}
	if (user.textrans) {







|







355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
	}
	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)		// only one error message per symbol
			return
		s.err = true
	}
	if (user.textrans) {
︙︙
Changes to core/deco.js.
︙︙
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

var	dd_tb = {},		// definition of the decorations
	a_de,			// array of the decoration elements
	cross			// cross voice decorations

// decorations - populate with standard decorations
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",		// (staccatissimo or spiccato)
	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",







|

|





|
|







53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

var	dd_tb = {},		// definition of the decorations
	a_de,			// array of the decoration elements
	cross			// cross voice decorations

// decorations - populate with standard decorations
var decos = {
	dot: "0 stc 6 .7 1",
	tenuto: "0 emb 6 4 3",
	slide: "1 sld 10 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 12,2 3 7",
	downbow: "3 dnb 8,2 4 6",
	gmark: "3 grm 7 4 6",
	wedge: "0 wedge 8 1.5 1",		// (staccatissimo or spiccato)
	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",
︙︙
171
172
173
174
175
176
177
178

179
180
181
182
183
184
185
186
187
188
189
190
191
	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"},


	// types of decoration per function
	f_near = [
		d_near,		// 0 - near the note
		d_slide,	// 1
		d_arp		// 2
	],
	f_note = [
		null, null, null,
		d_upstaff,	// 3 - tied to note
		d_upstaff	// 4 (below the staff)
	],
	f_staff = [







|
>




|
|







171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
	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",
	fg: "45 0 0 0 0"},

	// types of decoration per function
	f_near = [
		d_near,		// 0 - near the note
		d_slide,	// 1 - slide or tied to the note stem
		d_arp		// 2 - arpeggio
	],
	f_note = [
		null, null, null,
		d_upstaff,	// 3 - tied to note
		d_upstaff	// 4 (below the staff)
	],
	f_staff = [
︙︙
261
262
263
264
265
266
267



268
269
270
271
272
273
274
function up3(s, pos) {
	switch (pos & 0x07) {
	case C.SL_ABOVE:
		return 1	// true
	case C.SL_BELOW:
		return 0	// false
	}



//	if (s.multi)
//		return s.multi > 0
//	return 1		// true
	return !s.second
} // up3()

// - of the dynamic and volume marks







>
>
>







262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
function up3(s, pos) {
	switch (pos & 0x07) {
	case C.SL_ABOVE:
		return 1	// true
	case C.SL_BELOW:
		return 0	// false
	}
	if (!s.multi		// if voice alone in the staff
	 && !s.invis)		// but not invisible symbol of a secondary voice
		return 1 //true
//	if (s.multi)
//		return s.multi > 0
//	return 1		// true
	return !s.second
} // up3()

// - of the dynamic and volume marks
︙︙
336
337
338
339
340
341
342







343

344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384

385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400












401

402





403









404
405



406
407
408
409
410
411
412
		dd = de.dd

	y = up ? s.ymx : s.ymn
	if (y > 0 && y < 24) {
		y = (((y + 9) / 6) | 0) * 6 - 6	// between lines
	}
	if (up) {







		y += dd.hd

		s.ymx = y + dd.h
	} else if (dd.name[0] == 'w') {		// wedge (no descent)
		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 dot (staccato)
	    if (!(s.beam_st && s.beam_end)) {	// if in a beam sequence
		if (up) {
			if (s.stem > 0)
				de.x += 3.5	// stem_xoff
		} 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	// put the dot a bit lower
				s.ymx = de.y + dd.h
			}
		}
	    }
	}
}

/* 1: special case for slide */
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 = s.x - xc;









	de.x -= xc;
	de.y = 3 * (yc - 18)



}

// special case for long decoration
function d_trill(de) {
	if (de.ldst)
		return
    var	y, w, tmp,







>
>
>
>
>
>
>
|
>
|












<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




|
|
|
<

>















|
>
>
>
>
>
>
>
>
>
>
>
>
|
>
|
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
|
|
>
>
>







340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368



















369
370
371
372
373
374
375

376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
		dd = de.dd

	y = up ? s.ymx : s.ymn
	if (y > 0 && y < 24) {
		y = (((y + 9) / 6) | 0) * 6 - 6	// between lines
	}
	if (up) {
		if (s.ys > 27
		 && dd.name[0] == 'd'		// if dot (staccato)
		 && s.a_dd[0].name == "dot"	// as the first decoration
		 && s.stem > 0 && s.nflags >= 0
		 && s.beam_st && s.beam_end)
			y -= 6			// put the dot a bit lower
		else
			y += dd.hd
		if (s.ymx < y + dd.h)
			s.ymx = y + dd.h
	} else if (dd.name[0] == 'w') {		// wedge (no descent)
		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



















}

/* 1: special case for slide */
function d_slide(de) {
    var	m, dx, xc,
	s = de.s,
	yc = s.y


      if (de.dd.glyph == "sld") {		// !slide!
	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 > 5)
			xc = -dx
		else
			xc = -5
	}
      } else {					// decoration tied to the stem
		if (de.s.stem >= 0) {
			if (s.nflags >= -1) {
				xc = 3.5
				yc = s.ys
				if (s.nflags > 1)
					yc -= 4 * (s.nflags - 1)
			} else {
				xc = 0
				yc = s.y + 21
			}
			yc = (yc + 3 * (s.notes[s.nhd].pit - 18)) / 2
		} else {
			de.rotpi = 1//true	// rotate pi (180°)
			if (s.nflags >= -1) {
				xc = -3.5
				yc = s.ys
				if (s.nflags > 1)
					yc += 4 * (s.nflags - 1)
			} else {
				xc = 0
				yc = s.y - 21
			}
			yc = (yc + 3 * (s.notes[0].pit - 18)) / 2
		}
      }
	de.x += xc;
	de.y = yc

	if (de.y < 0)
		y_set(s.st, 0, de.x, de.dd.wl, de.y - de.dd.h)
}

// special case for long decoration
function d_trill(de) {
	if (de.ldst)
		return
    var	y, w, tmp,
︙︙
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574

575



576
577
578
579
580
581
582
	s = de.s,
	dd = de.dd,
	x = de.x,
	w = dd.wl + dd.wr

	// glyphs inside the staff
	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 < s.ymx)
				y = s.ymx
			for (s = s.ts_next; s; s = s.ts_next)
				if (s.seqst)
					break
			x += ((s ? s.x : realwidth) - x) * .45
		}







<
<





<
|

>
|
>
>
>







581
582
583
584
585
586
587


588
589
590
591
592

593
594
595
596
597
598
599
600
601
602
603
604
605
606
	s = de.s,
	dd = de.dd,
	x = de.x,
	w = dd.wl + dd.wr

	// glyphs inside the staff
	switch (dd.glyph) {


	case "lphr":
	case "mphr":
	case "sphr":
	case "short":
	case "tick":

		if (s.type == C.BAR)
			s.invis = 1
		// fall thru
	case "brth":
	case "caes":
		y = staff_tb[s.st].topbar + 2 + dd.hd
		if (!s.invis) {
			if (dd.glyph == "brth" && y < s.ymx)
				y = s.ymx
			for (s = s.ts_next; s; s = s.ts_next)
				if (s.seqst)
					break
			x += ((s ? s.x : realwidth) - x) * .45
		}
︙︙
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
		wr = parseFloat(a[5])

	if (isNaN(c_func)) {
		error(1, null, "%%deco: bad C function value '$1'", a[1])
		return //undefined
	}
	if (c_func > 10
	 && (c_func < 32 || c_func > 44)) {
		error(1, null, "%%deco: bad C function index '$1'", c_func)
		return //undefined
	}
//	if (c_func == 5)			// old !trill(!
//		c_func = 3
//	if (c_func == 7)			// old !cresc(!
//		c_func = 6







|







698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
		wr = parseFloat(a[5])

	if (isNaN(c_func)) {
		error(1, null, "%%deco: bad C function value '$1'", a[1])
		return //undefined
	}
	if (c_func > 10
	 && (c_func < 32 || c_func > 45)) {
		error(1, null, "%%deco: bad C function index '$1'", c_func)
		return //undefined
	}
//	if (c_func == 5)			// old !trill(!
//		c_func = 3
//	if (c_func == 7)			// old !cresc(!
//		c_func = 6
︙︙
764
765
766
767
768
769
770



771
772
773
774
775
776
777
778
779
780
781
782
783
784
785

786
787
788
789
790
791
792
793
    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		// keep the start/end
		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
	}
} // do_ctie()

// get/create the definition of a decoration







>
>
>












|


>
|







788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
    var	nt2 = cross[nm],
	nm2 = nm.slice(0, -1) + (nm.slice(-1) == '(' ? ')' : '(')

	if (nt2) {
		error(1, s, "Conflict on !$1!", nm)
		return
	}
	if (nt1.tie_ty)			// if normal '-'
		curvoice.tie_s = null

	nt1.s = s
	nt2 = cross[nm2]
	if (!nt2) {
		cross[nm] = nt1		// keep the start/end
		return
	}
	if (nm.slice(-1) == ')') {
		nt2 = nt1
		nt1 = cross[nm2]
	}
	cross[nm2] = null
	if (nt1.midi != nt2.midi
	 || nt1.s.time + nt1.dur != nt2.s.time) {
		error(1, s, "Bad tie")
	} else {
		if (!nt1.tie_ty)		// if not normal '-'
			nt1.tie_ty = C.SL_AUTO
		nt1.tie_e = nt2
		nt2.tie_s = nt1
		nt1.s.ti1 = nt2.s.ti2 = true
	}
} // do_ctie()

// get/create the definition of a decoration
︙︙
822
823
824
825
826
827
828
829

































830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847



848
849
850
851
852









853
854
855
856
857
858
859
		dd.ty = ty
	}
	return dd
} // get_dd()

/* -- convert the decorations -- */
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

		/* special decorations */
		switch (dd.func) {
		case 0:			// near
			if (s.type == C.BAR && nm == "dot") {
				s.bar_dotted = true
				continue
			}
			// fall thru
		case 1:			// slide



		case 2:			// arp
//			if (s.type != C.NOTE && s.type != C.REST) {
			if (!s.notes) {
				error(1, s, errs.must_note_rest, nm)
				continue









			}
			break
		case 4:			// below the staff
		case 5:			// above the staff
			i = nm.match(/1?[85]([vm])([ab])([()])/)
			if (i) {				// if ottava
				j = i[1] == 'v' ? 1 : 2







|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

















|
>
>
>





>
>
>
>
>
>
>
>
>







850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
		dd.ty = ty
	}
	return dd
} // get_dd()

/* -- convert the decorations -- */
function deco_cnv(s, prev) {
    var	i, j, dd, nm, note, s1, court, fg

	// mark a finger glissando
	function sav_fg() {
	    var	i,
		s1 = prev

		if (s.type != C.NOTE)
			return 1
		while (s1 && s1.type != C.NOTE)
			s1 = s1.prev
		if (!s1)
			return 1
		for (i = 0; i < s1.a_dd.length; i++) {
			if (s1.a_dd[i].name == dd.name) {
				if (!s.fg)
					s.fg = []
				s.fg.push({
					ty: 1,		// end of glissando
					s: s1,
					nm: dd.name
				})
				if (!s1.fg)
					s1.fg = []
				s1.fg.push({
					ty: 0,		// start of glissando
					s: s,
					nm: dd.name
				})
				return 0
			}
		}
		return 1
	} // sav_fg()

	while (1) {
		nm = a_dcn.shift()
		if (!nm)
			break
		dd = get_dd(nm)
		if (!dd)
			continue

		/* special decorations */
		switch (dd.func) {
		case 0:			// near
			if (s.type == C.BAR && nm == "dot") {
				s.bar_dotted = true
				continue
			}
			// fall thru
		case 1:			// slide & deco on stem
			if (nm != "slide")
				s.decstm=dd.h		// deco stem
			// fall thru
		case 2:			// arp
//			if (s.type != C.NOTE && s.type != C.REST) {
			if (!s.notes) {
				error(1, s, errs.must_note_rest, nm)
				continue
			}
			break
		case 3:
			if (fg && dd.glyph == "fng") { // move the fingers out of staves
				for (i = 0; i <= 5; i++) {
					decos[i.toString()] = "5 fng 5,5 3 3 " + i
					if (dd_tb[i.toString()])
						dd_tb[i.toString()].func = 5
				}
			}
			break
		case 4:			// below the staff
		case 5:			// above the staff
			i = nm.match(/1?[85]([vm])([ab])([()])/)
			if (i) {				// if ottava
				j = i[1] == 'v' ? 1 : 2
︙︙
1002
1003
1004
1005
1006
1007
1008



1009
1010
1011











1012
1013
1014
1015
1016
1017
1018
		case 44:		// cross-voice ties
			if (s.type != C.NOTE) {
				error(1, s, errs.must_note, nm)
				continue
			}
			do_ctie(nm, s, s.notes[0])	// (only one note for now)
			continue



//		default:
//			break
		}












		// add the decoration in the symbol
		if (!s.a_dd)
			s.a_dd = []
		s.a_dd.push(dd)
	}
	// handle the possible courtesy accidental







>
>
>



>
>
>
>
>
>
>
>
>
>
>







1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
		case 44:		// cross-voice ties
			if (s.type != C.NOTE) {
				error(1, s, errs.must_note, nm)
				continue
			}
			do_ctie(nm, s, s.notes[0])	// (only one note for now)
			continue
		case 45:		// finger glissando
			fg = 1 //true
			continue
//		default:
//			break
		}

		// handle the fingering in case finger glissando
		if (fg && dd.glyph == 'fng') {
			fg = 0 //false
			if (sav_fg()) {
				error(1, s,
					"!$1! must be on the last of a couple of notes",
					nm)
				continue
			}
		}

		// add the decoration in the symbol
		if (!s.a_dd)
			s.a_dd = []
		s.a_dd.push(dd)
	}
	// handle the possible courtesy accidental
︙︙
1158
1159
1160
1161
1162
1163
1164











































1165
1166
1167
1168
1169
1170
1171
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
		}







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
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 = []

	// display a finger glissando
	function out_fg() {
	    var	k, l, de2, fg, fg2, x2,
		j = s.fg.length

		while (--j >= 0) {
			fg = s.fg[j]
			if (fg.nm == dd.name)
				break
		}
		if (j < 0)
			return

		if (fg.ty) {		// end
			if (fg.ty == 1)		// no start (not treated yet)
				out_wln(x - 19, y, 12)
			return
		}

		x2 = x + 7		// start
		for (k = 0; k < a_de.length; k++) {
			de2 = a_de[k]
			if (de2.s != fg.s
			 || de2.dd.name != dd.name)
				continue
			for (l = 0; l < de2.s.fg.length; l++) {
				fg2 = de2.s.fg[l]
				if (fg2.nm == fg.nm)
					break
			}
			if (fg2.nm == fg.nm) {		// if same finger
				fg2.ty = 2		// end done
				xypath(x2, y + 1)
				output += 'l' + (de2.x - 7 - x2).toFixed(1)
					+ ' ' + (y - de2.y
						- staff_tb[s.st].y).toFixed(1)
					+ '" stroke-width=".7"/>\n'
				return
			}
		}
		out_wln(x2, y, 12)	// start without end
	} // out_fg()

		st = nstaff;
		y = staff_tb[st].y
		while (--st >= 0) {
			y2 = staff_tb[st].y;
			ymid[st] = (y + 24 + y2) * .5;
			y = y2
		}
︙︙
1267
1268
1269
1270
1271
1272
1273



1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286


1287
1288
1289
1290
1291
1292
1293
//			g_open(x, y, 0, .7, de.inv ? -.7 : 0);
//			x = y = 0
//		} else
		if (de.inv) {
			y = y + dd.h - dd.hd
			g_open(x, y, 0, 1, -1);
			x = y = 0



		}
		if (de.has_val) {
			if (dd.func != 2	// if not !arpeggio!
			 || stv_g.st < 0)	// or not staff scale
// || voice_tb[s.v].scale != 1)
				out_deco_val(x, y, f, de.val / stv_g.scale, de.defl)
			else
				out_deco_val(x, y, f, de.val, de.defl)
			if (de.cont)
				new_de.push(de.start)	// to be continued next line
		} else if (dd.str != undefined		// string
			&& !tgls[dd.glyph]
			&& !glyphs[dd.glyph]) {		// with a class


			out_deco_str(x, y,		// - dd.h * .2,
					de)
		} else if (de.lden) {
			out_deco_long(x, y, de)
		} else {
			xygl(x, y, f)
		}







>
>
>













>
>







1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
//			g_open(x, y, 0, .7, de.inv ? -.7 : 0);
//			x = y = 0
//		} else
		if (de.inv) {
			y = y + dd.h - dd.hd
			g_open(x, y, 0, 1, -1);
			x = y = 0
		} else if (de.rotpi) {
			g_open(x, y, 180)
			x = y = 0
		}
		if (de.has_val) {
			if (dd.func != 2	// if not !arpeggio!
			 || stv_g.st < 0)	// or not staff scale
// || voice_tb[s.v].scale != 1)
				out_deco_val(x, y, f, de.val / stv_g.scale, de.defl)
			else
				out_deco_val(x, y, f, de.val, de.defl)
			if (de.cont)
				new_de.push(de.start)	// to be continued next line
		} else if (dd.str != undefined		// string
			&& !tgls[dd.glyph]
			&& !glyphs[dd.glyph]) {		// with a class
			if (s.fg)			// if finger glissando
				out_fg()		// (may change y)
			out_deco_str(x, y,		// - dd.h * .2,
					de)
		} else if (de.lden) {
			out_deco_long(x, y, de)
		} else {
			xygl(x, y, f)
		}
︙︙
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
	if (font_size)
		param_set_font("measurefont", "* " + font_size.toString());
}

/* -- draw the parts and the tempo information -- */
// (unscaled delayed output)
function draw_partempo() {
    var	s, s2, some_part, some_tempo, h, w, y, st,
	sy = cur_sy

	// search the top staff
	for (st = 0; st <= nstaff; st++) {
		if (sy.st_print[st])
			break
	}







|







2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
	if (font_size)
		param_set_font("measurefont", "* " + font_size.toString());
}

/* -- draw the parts and the tempo information -- */
// (unscaled delayed output)
function draw_partempo() {
    var	s, s2, some_part, some_tempo, h, w, y, st, p,
	sy = cur_sy

	// search the top staff
	for (st = 0; st <= nstaff; st++) {
		if (sy.st_print[st])
			break
	}
︙︙
2043
2044
2045
2046
2047
2048
2049



2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061

2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072

2073

2074
2075
2076
2077
2078
2079
2080
			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 (ymin < y)
			ymin = y
	}
	if (some_part) {
		set_sscale(-1)
		ymin *= staff_tb[st].staffscale
		for (s = some_part; s; s = s.ts_next) {
			s2 = s.part
			if (!s2 || s2.invis)
				continue

			w = strwh(s2.text)[0]
			if (user.anno_start || user.anno_stop) {
				s2.wl = 0
				s2.wr = w
				s2.ymn = ymin
				s2.ymx = s2.ymn + h
				anno_start(s2)
			}
			xy_str(s2.x,
				ymin + 2 + gene.curfont.pad + gene.curfont.size * .22,
				s2.text)

			y_set(st, 1, s2.x, w + 3, ymin + 2 + h)

			if (s2.x < 0)
				yn = ymin + 2 + h
			anno_stop(s2)
		}
	}

	// output the tempos







>
>
>
|











>
|









<
>
|
>







2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210

2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
			some_part = s;
			set_font("parts");
			h = gene.curfont.size + 2 +
				gene.curfont.pad * 2
		}
		if (s2.x == undefined)
			s2.x = s.x - 10
		p = s2.text
		if (cfmt.partname)
			s2.ntxt = p = partname(p)[2]
		w = strwh(p)[0]
		y = y_get(st, true, s2.x, w + 3)
		if (ymin < y)
			ymin = y
	}
	if (some_part) {
		set_sscale(-1)
		ymin *= staff_tb[st].staffscale
		for (s = some_part; s; s = s.ts_next) {
			s2 = s.part
			if (!s2 || s2.invis)
				continue
			p = s2.ntxt || s2.text
			w = strwh(p)[0]
			if (user.anno_start || user.anno_stop) {
				s2.wl = 0
				s2.wr = w
				s2.ymn = ymin
				s2.ymx = s2.ymn + h
				anno_start(s2)
			}
			xy_str(s2.x,
				ymin + 2 + gene.curfont.pad + gene.curfont.size * .22,

				p)
			y_set(st, 1, s2.x, w + 3,
				(ymin + 2 + h) / staff_tb[st].staffscale)
			if (s2.x < 0)
				yn = ymin + 2 + h
			anno_stop(s2)
		}
	}

	// output the tempos
︙︙
2119
2120
2121
2122
2123
2124
2125
2126

2127
2128
2129
2130
				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
		}
	}
}







|
>




2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
				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) / staff_tb[st].staffscale)
			dosh >>= 1
		}
	}
}
Changes to core/draw.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - draw.js - draw functions
//
// Copyright (C) 2014-2023 Jean-Francois Moine
//
// This file is part of abc2svg-core.
//
// abc2svg-core is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.


|







1
2
3
4
5
6
7
8
9
10
// abc2svg - draw.js - draw functions
//
// Copyright (C) 2014-2024 Jean-Francois Moine
//
// This file is part of abc2svg-core.
//
// abc2svg-core is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
︙︙
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
	STEM_CH_MIN2	= 10,	/* ... for notes with two beams */
	STEM_CH_MIN3	= 9,	/* ... for notes with three beams */
	STEM_CH_MIN4	= 9,	/* ... for notes with four beams */
	BEAM_DEPTH	= 3.2,	/* width of a beam stroke */
	BEAM_OFFSET	= .25,	/* pos of flat beam relative to staff line */
	BEAM_SHIFT	= 5,	/* shift of second and third beams */
	BEAM_STUB	= 7,	/* length of stub for flag under beam */ 
	SLUR_SLOPE	= .5,	/* max slope of a slur */
	GSTEM		= 15,	/* grace note stem length */
	GSTEM_XOFF	= 2.3	/* x offset for grace note stem */

    var cache,
	anno_a = []		// symbols with annotations

/* -- compute the best vertical offset for the beams -- */







|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
	STEM_CH_MIN2	= 10,	/* ... for notes with two beams */
	STEM_CH_MIN3	= 9,	/* ... for notes with three beams */
	STEM_CH_MIN4	= 9,	/* ... for notes with four beams */
	BEAM_DEPTH	= 3.2,	/* width of a beam stroke */
	BEAM_OFFSET	= .25,	/* pos of flat beam relative to staff line */
	BEAM_SHIFT	= 5,	/* shift of second and third beams */
	BEAM_STUB	= 7,	/* length of stub for flag under beam */ 
	SLUR_SLOPE	= .7,	// max slope of a slur
	GSTEM		= 15,	/* grace note stem length */
	GSTEM_XOFF	= 2.3	/* x offset for grace note stem */

    var cache,
	anno_a = []		// symbols with annotations

/* -- compute the best vertical offset for the beams -- */
︙︙
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
			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)		/* shift flat beams onto staff lines */
		b += b_pos(s1.grace, s1.stem, nflags, b - staff_tb[st].y)

	/* adjust final stems and rests under beam */
	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
//fixme: hack
				if (s.ts_prev
				 && s.ts_prev.stem > 0
				 && s.ts_prev.st == s.st
				 && s.ts_prev.ymn < s.ymx
				 && s.ts_prev.x == s.x
				 && s.notes[0].shhd == 0) {
					s.ts_prev.x -= 3;	/* fix stem clash */
					s.ts_prev.xs -= 3
				}
			} else {
				s.ymn = s.ys - 2.5
			}
			break
		case C.REST:
			y = a * s.x + b - staff_tb[s.st].y
			dy = BEAM_DEPTH + BEAM_SHIFT * (nflags - 1)







<





<




















<
<
<
<
<
<
<
<
<
<







395
396
397
398
399
400
401

402
403
404
405
406

407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426










427
428
429
430
431
432
433
			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)

					if (y > 0)
						b += y
				} else {
					y = g.ymn - y
						- BEAM_DEPTH - BEAM_SHIFT * (nflags - 1)

					if (y < 0)
						b += y
				}
			}
			break
		}
		if (s == s2)
			break
	}

	if (a == 0)		/* shift flat beams onto staff lines */
		b += b_pos(s1.grace, s1.stem, nflags, b - staff_tb[st].y)

	/* adjust final stems and rests under beam */
	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










			} else {
				s.ymn = s.ys - 2.5
			}
			break
		case C.REST:
			y = a * s.x + b - staff_tb[s.st].y
			dy = BEAM_DEPTH + BEAM_SHIFT * (nflags - 1)
︙︙
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
				}
			}
		}

		y1 = bm.a * x1 + bm.b - dy;
		x2 -= x1;
		x2 /= stv_g.scale;
		dy2 = bm.a * x2 * stv_g.scale
		xypath(x1, y1, true);
		output += 'l' + x2.toFixed(1) + ' ' + (-dy2).toFixed(1) +
			'v' + h.toFixed(1) +
			'l' + (-x2).toFixed(1) + ' ' + dy2.toFixed(1) +
			'z"/>\n'
	} // draw_beam()








|







497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
				}
			}
		}

		y1 = bm.a * x1 + bm.b - dy;
		x2 -= x1;
		x2 /= stv_g.scale;
		dy2 = bm.a * x2 * stv_g.stsc
		xypath(x1, y1, true);
		output += 'l' + x2.toFixed(1) + ' ' + (-dy2).toFixed(1) +
			'v' + h.toFixed(1) +
			'l' + (-x2).toFixed(1) + ' ' + dy2.toFixed(1) +
			'z"/>\n'
	} // draw_beam()

︙︙
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763























764






765
766
767
768
769
770


771
772
773




774



775
776
777
778
779
780
781
	}
}

/* -- draw the time signature -- */
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;

	// adjust the vertical offset according to the staff definition
	if (p_staff.stafflines != '|||||')
		y += (p_staff.topbar + p_staff.botbar) / 2 - 12	// bottom

	for (i = 0; i < s.a_meter.length; i++) {
		meter = s.a_meter[i];
		x = s.x + s.x_meter[i]






























		if (meter.bot) {
			out_XYAB('\
<g transform="translate(X,Y)" text-anchor="middle">\n\
	<text y="-12">A</text>\n\
	<text>B</text>\n\
</g>\n', x, y + 6, m_gl(meter.top), m_gl(meter.bot))


		} else {
			out_XYAB('\
<text x="X" y="Y" text-anchor="middle">A</text>\n',




				x, y + 12, m_gl(meter.top))



		}
	}
}

    var	acc_nd = {}		// cache of the microtonal accidentals

/* -- draw an accidental -- */







|
<
|
|








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>

<
<
<
|
|
>
>
|
<
<
>
>
>
>
|
>
>
>







733
734
735
736
737
738
739
740

741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781



782
783
784
785
786


787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
	}
}

/* -- draw the time signature -- */
function draw_meter(s) {
	if (!s.a_meter)
		return
    var	i, m, meter, x, x0, yt,

	p_staff = staff_tb[s.st],
	y = p_staff.y

	// adjust the vertical offset according to the staff definition
	if (p_staff.stafflines != '|||||')
		y += (p_staff.topbar + p_staff.botbar) / 2 - 12	// bottom

	for (i = 0; i < s.a_meter.length; i++) {
		meter = s.a_meter[i];
		x = s.x + s.x_meter[i]
		yt = y + (meter.bot ? 18 : 12)
		xygl(x, yt, "mtr" + meter.top[0])
		if (meter.top.length > 1) {
			m = 0
			x0 = x
			while (1) {
				switch (meter.top[m]) {
				case '(':
				case ')':
					x += 4
					break
				case '1':
					x += 8
					break
				case ' ':
					x += 4
					break
				case '+':
					x += 2
					// fall thru
				default:
					x += 10
					break
				}
				if (++m >= meter.top.length)
					break
				xygl(x, yt, "mtr" + meter.top[m])
			}
			x = (x0 + x) / 2 - 5
		}
		if (meter.bot) {



			if (meter.bot[1]) {
				if (meter.bot[0] == 1) {
					x0 = x - 4
					x += 4
				} else {


					x0 = x - 5
					x += 5
				}
				xygl(x0, y + 6, "mtr" + meter.bot[0])
				xygl(x, y + 6, "mtr" + meter.bot[1])
			} else {
				xygl(x, y + 6, "mtr" + meter.bot[0])
			}
		}
	}
}

    var	acc_nd = {}		// cache of the microtonal accidentals

/* -- draw an accidental -- */
︙︙
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
	}
	n = hla.length
	if (!n)
		return			// no

	// handle the helper lines out of the staff
    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

	// get the x start and x stop of the intermediate helper lines
	note = s.notes[s.stem < 0 ? s.nhd : 0]
	shhd = note.shhd








|


|







869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
	}
	n = hla.length
	if (!n)
		return			// no

	// handle the helper lines out of the staff
    var	dx1, dx2, hl, shhd,hlp,
	stafflines = cur_sy.staves[st].stafflines,
	top = stafflines.length - 1,
	yu =  top,
	bot = (p_staff.hll - 17) / 2,
	yl = bot,
	dx = s.grace ? 4 : hw_tb[s.head] * 1.3

	// get the x start and x stop of the intermediate helper lines
	note = s.notes[s.stem < 0 ? s.nhd : 0]
	shhd = note.shhd

︙︙
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
	/* draw the head */
	if (note.invis) {
		;
	} else if (s.grace) {			// don't apply %%map to grace notes
		p = "ghd";
		x_note -= 4.5 * stv_g.scale
	} else if (note.map && note.map[0]) {
		i = s.head;
		p = note.map[0][i]		// heads
		if (!p)
			p = note.map[0][note.map[0].length - 1]
		i = p.indexOf('/')
		if (i >= 0) {			// stem dependant
			if (s.stem >= 0)
				p = p.slice(0, i)







|







1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
	/* draw the head */
	if (note.invis) {
		;
	} else if (s.grace) {			// don't apply %%map to grace notes
		p = "ghd";
		x_note -= 4.5 * stv_g.scale
	} else if (note.map && note.map[0]) {
		i = head;
		p = note.map[0][i]		// heads
		if (!p)
			p = note.map[0][note.map[0].length - 1]
		i = p.indexOf('/')
		if (i >= 0) {			// stem dependant
			if (s.stem >= 0)
				p = p.slice(0, i)
︙︙
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
						} else {
							y1 = k1.ys + 6
						}
					} else {
						y1 = k1.ys - 3
					}
				} else {
					y1 = k1.y - 8
				}
			}
		}
	}

	if (sl.nte) {					// slur ending on a note
		y2 = 3 * (sl.nte.pit - 18) + 2 * dir







|







2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
						} else {
							y1 = k1.ys + 6
						}
					} else {
						y1 = k1.ys - 3
					}
				} else {
					y1 = k1.y - 5
				}
			}
		}
	}

	if (sl.nte) {					// slur ending on a note
		y2 = 3 * (sl.nte.pit - 18) + 2 * dir
︙︙
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
					 && k2.nflags >= -1
					 && !k2.in_tuplet)
//						|| k2.ys < y2 + 3))
						y2 = k2.ys + 6
					else
						y2 = k2.ys - 3
				} else {
					y2 = k2.y - 8
				}
			}
		}
	}

	if (k1.type != C.NOTE) {
		y1 = y2 + 1.2 * dir;







|







2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
					 && k2.nflags >= -1
					 && !k2.in_tuplet)
//						|| k2.ys < y2 + 3))
						y2 = k2.ys + 6
					else
						y2 = k2.ys - 3
				} else {
					y2 = k2.y - 5
				}
			}
		}
	}

	if (k1.type != C.NOTE) {
		y1 = y2 + 1.2 * dir;
︙︙
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
		y1 += dy
	}

	/* special case for grace notes */
	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; i < i2; i++) {







|







2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
		y1 += dy
	}

	/* special case for grace notes */
	if (k1.grace)
		x1 = k1.x - GSTEM_XOFF * .5
	if (k2.grace)
		x2 = k2.x + GSTEM_XOFF * .5

	h = 0;
	a = (y2 - y1) / (x2 - x1)
	if (k1 != k2
	 && k1.v == k2.v) {
	    addy = y1 - a * x1
	    for (i = 1; i < i2; i++) {
︙︙
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
				if (y < h)
					h = y
			}
			break
		case C.GRACE:
			for (g = k.extra; g; g = g.next) {
				if (dir > 0) {
					y = 3 * (g.notes[g.nhd].pit - 18) + 6
					if (y < g.ymx)
						y = g.ymx;
					y -= a * g.x + addy
					if (y > h)
						h = y
				} else {
					y = 3 * (g.notes[0].pit - 18) - 6
					if (y > g.ymn)
						y = g.ymn;
					y -= a * g.x + addy
					if (y < h)
						h = y
				}
			}
			break







|
|





|
|







2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
				if (y < h)
					h = y
			}
			break
		case C.GRACE:
			for (g = k.extra; g; g = g.next) {
				if (dir > 0) {
//					y = 3 * (g.notes[g.nhd].pit - 18) + 6
//					if (y < g.ymx)
						y = g.ymx;
					y -= a * g.x + addy
					if (y > h)
						h = y
				} else {
//					y = 3 * (g.notes[0].pit - 18) - 6
//					if (y > g.ymn)
						y = g.ymn;
					y -= a * g.x + addy
					if (y < h)
						h = y
				}
			}
			break
︙︙
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
	dx = x2 - x1;
	a = (y2 - y1) / dx;
/*fixme: it seems to work with .4, but why?*/
//	addy = y1 - a * x1 + .4 * height
//fixme: the following code seems better!
	addy = y1 - a * x1
	if (height > 0)
		addy += 4 * Math.sqrt(height) - 2
	else
		addy -= 4 * Math.sqrt(-height) - 2
	for (i = 0; i < i2; i++) {
		k = path[i]
		if (k.st != k1.st || k.type == C.BAR)
			continue
		y = a * k.x + addy
		if (k.ymx < y)
			k.ymx = y
		else if (k.ymn > y)
			k.ymn = y
		if (recurr)			// no room when slur on 2 staves
			continue
		if (i == i2 - 1) {
			dx = x2
			if (sl.nte)
				dx -= 5;
		} else {
			dx = k.x + k.wr
		}
		if (i != 0)







|

|
|










|







2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
	dx = x2 - x1;
	a = (y2 - y1) / dx;
/*fixme: it seems to work with .4, but why?*/
//	addy = y1 - a * x1 + .4 * height
//fixme: the following code seems better!
	addy = y1 - a * x1
	if (height > 0)
		addy += 3 * Math.sqrt(height) - 2
	else
		addy -= 3 * Math.sqrt(-height) - 2
	for (i = 0; i <= i2; i++) {
		k = path[i]
		if (k.st != k1.st || k.type == C.BAR)
			continue
		y = a * k.x + addy
		if (k.ymx < y)
			k.ymx = y
		else if (k.ymn > y)
			k.ymn = y
		if (recurr)			// no room when slur on 2 staves
			continue
		if (i == i2) {
			dx = x2
			if (sl.nte)
				dx -= 5;
		} else {
			dx = k.x + k.wr
		}
		if (i != 0)
︙︙
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883

2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894

2895
2896
2897
2898
2899
2900
2901
	}

	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) + /* 1.0 * */ dir

	h = (.03 * (x2 - x1) + 16) * dir * s1.fmt.tieheight
//	anno_start(k1, 'slur')
	slur_out(x1, y, x2, y, dir, h, not1.tie_ty & C.SL_DOTTED)
//	anno_stop(k1, 'slur')
} // draw_tie()

/* -- draw all ties between neighboring notes -- */
function draw_all_ties(p_voice) {
    var s, s1, s2, clef_chg, x, dx, m, not1, not2,
	tim2 = 0

	/* search the start of ties */
//	clef_chg = false
	s1 = p_voice.sym
	set_color(s1.color)
	for ( ; s1; s1 = s1.next) {
		if (s1.ti2			// if end of tie

		 && s1.time != tim2) {		// and new end
			for (m = 0; m <= s1.nhd; m++) {
				not2 = s1.notes[m]
				not1 = not2.tie_s
				if (!not1
				 || not1.s.v != s1.v)	// (already done)
					continue
				draw_tie(not1, not2, 1)
			}
		}
		if (!s1.ti1)			// if not start of tie

			continue

		// get the end of the tie(s)
		if (s1.type == C.GRACE) {
			for (s = s1.extra; s; s = s.next) {
			    for (m = 0; m <= s1.nhd; m++) {
				not1 = s.notes[m]







|
<
|
















>










|
>







2878
2879
2880
2881
2882
2883
2884
2885

2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
	}

	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) + .8 * dir

	h = (.15 * (x2 - x1) + 14) * dir * s1.fmt.tieheight
//	anno_start(k1, 'slur')
	slur_out(x1, y, x2, y, dir, h, not1.tie_ty & C.SL_DOTTED)
//	anno_stop(k1, 'slur')
} // draw_tie()

/* -- draw all ties between neighboring notes -- */
function draw_all_ties(p_voice) {
    var s, s1, s2, clef_chg, x, dx, m, not1, not2,
	tim2 = 0

	/* search the start of ties */
//	clef_chg = false
	s1 = p_voice.sym
	set_color(s1.color)
	for ( ; s1; s1 = s1.next) {
		if (s1.ti2			// if end of tie
		 && !s1.invis
		 && s1.time != tim2) {		// and new end
			for (m = 0; m <= s1.nhd; m++) {
				not2 = s1.notes[m]
				not1 = not2.tie_s
				if (!not1
				 || not1.s.v != s1.v)	// (already done)
					continue
				draw_tie(not1, not2, 1)
			}
		}
		if (!s1.ti1			// if not start of tie
		 || s1.invis)
			continue

		// get the end of the tie(s)
		if (s1.type == C.GRACE) {
			for (s = s1.extra; s; s = s.next) {
			    for (m = 0; m <= s1.nhd; m++) {
				not1 = s.notes[m]
︙︙
3072
3073
3074
3075
3076
3077
3078




3079
3080
3081
3082
3083
3084
3085
			continue
		case C.MREST:
			y_set(s.st, true, s.x + 16, 32, s.ymx + 2)
			continue
		default:
			y_set(s.st, true, s.x - s.wl, s.wl + s.wr, s.ymx + 2);
			y_set(s.st, false, s.x - s.wl, s.wl + s.wr, s.ymn - 2)




			continue
		case C.NOTE:
			break
		}

		// (permit closer staves)
		if (s.stem > 0) {







>
>
>
>







3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
			continue
		case C.MREST:
			y_set(s.st, true, s.x + 16, 32, s.ymx + 2)
			continue
		default:
			y_set(s.st, true, s.x - s.wl, s.wl + s.wr, s.ymx + 2);
			y_set(s.st, false, s.x - s.wl, s.wl + s.wr, s.ymn - 2)
			// fall thru
		case C.PART:
		case C.TEMPO:
		case C.STAVES:
			continue
		case C.NOTE:
			break
		}

		// (permit closer staves)
		if (s.stem > 0) {
︙︙
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v]
		if (!cur_sy.voices[v])
			continue
		st = cur_sy.voices[v].st
		if (!stl[st])
			continue
		if (!gene.vnt)
			continue
		p = gene.vnt == 2 ? p_voice.nm : p_voice.snm
		if (!p)
			continue
		delete p_voice.new_name
		if (!staff_d[st])
			staff_d[st] = p
		else
			staff_d[st] += "\n" + p







<
<
|







3236
3237
3238
3239
3240
3241
3242


3243
3244
3245
3246
3247
3248
3249
3250
	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v]
		if (!cur_sy.voices[v])
			continue
		st = cur_sy.voices[v].st
		if (!stl[st])
			continue


		p = gene.vnt == 1 ? p_voice.nm : p_voice.snm
		if (!p)
			continue
		delete p_voice.new_name
		if (!staff_d[st])
			staff_d[st] = p
		else
			staff_d[st] += "\n" + p
︙︙
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
	for (st = 0; st < staff_d.length; st++) {
		if (!staff_d[st])
			continue
		a_p = staff_d[st].split("\n");
		y = staff_tb[st].y
			+ staff_tb[st].topbar * .5
				* staff_tb[st].staffscale
			+ h2 * (a_p.length - 2)

		// if instrument with 2 staves, center the voice name
		if ((cur_sy.staves[st].flags & OPEN_BRACE)
		 && st + 1 < staff_tb.length
		 && (cur_sy.staves[st + 1].flags & CLOSE_BRACE)
		 && !staff_d[st + 1])
			y -= (staff_tb[st].y - staff_tb[st + 1].y) * .5







|







3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
	for (st = 0; st < staff_d.length; st++) {
		if (!staff_d[st])
			continue
		a_p = staff_d[st].split("\n");
		y = staff_tb[st].y
			+ staff_tb[st].topbar * .5
				* staff_tb[st].staffscale
			+ h2 * (a_p.length - 2) + h *.22

		// if instrument with 2 staves, center the voice name
		if ((cur_sy.staves[st].flags & OPEN_BRACE)
		 && st + 1 < staff_tb.length
		 && (cur_sy.staves[st + 1].flags & CLOSE_BRACE)
		 && !staff_d[st + 1])
			y -= (staff_tb[st].y - staff_tb[st + 1].y) * .5
︙︙
3393
3394
3395
3396
3397
3398
3399

3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414







3415



3416
3417

3418

3419


3420

























3421
3422
3423

















3424
3425
3426
3427
3428
3429
3430






3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
/* -- draw the staff systems and the measure bars -- */
function draw_systems(indent) {
	var	s, s2, st, x, x2, res, sy,
		xstaff = [],
		stl = [],		// all staves in the line
		bar_bot = [],
		bar_height = [],

		ba = [],		// bars [symbol, bottom, height]
		sb = "",
		thb = ""

	/* -- set the bottom and height of the measure bars -- */
	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]
		}

















	} // bar_set()

	/* -- draw a staff -- */
	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 // interline

		if (!/[\[|]/.test(stafflines))
			return				// no line
		w = x2 - x1;
		set_sscale(-1)

		// check if default staff
		if (cache && cache.st_l == stafflines
		 && staff_tb[st].staffscale == 1







>






|
|






|
>
>
>
>
>
>
>
|
>
>
>
|
|
>
|
>
|
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







>
>
>
>
>
>
|



|







3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
/* -- draw the staff systems and the measure bars -- */
function draw_systems(indent) {
	var	s, s2, st, x, x2, res, sy,
		xstaff = [],
		stl = [],		// all staves in the line
		bar_bot = [],
		bar_height = [],
		bar_ng = [],		// number of gaps
		ba = [],		// bars [symbol, bottom, height]
		sb = "",
		thb = ""

	/* -- set the bottom and height of the measure bars -- */
	function bar_set() {
	    var	st, sc, i, j, l, stlines, b, hlmap,
		dy = 0

		for (st = 0; st <= cur_sy.nstaff; st++) {
			if (xstaff[st] < 0) {
				bar_bot[st] = bar_height[st] = 0
				continue
			}
			sc = staff_tb[st].staffscale;
			stlines = cur_sy.staves[st].stafflines
			l = stlines.length
			for (i = 0; i < l; i++) {
				if (stlines[i] != '.' && stlines[i] != '-')
					break
			}
			if (i >= l - 1)			// 0 or 1 line
				bar_bot[st] = staff_tb[st].y + 6 * (l - 2) * sc
			else if (i == l - 2)		// 2 lines
				bar_bot[st] = staff_tb[st].y + 6 * (l - 3) * sc
			else
				bar_bot[st] = staff_tb[st].y + 6 * i * sc
			if (!dy) {
				if (i >= l - 2) {	// 0, 1 or 2 lines
					dy = staff_tb[st].y + 6 * l * sc
				} else {
					dy = staff_tb[st].y + 6 * (l - 1) * sc
				}
			}
			bar_height[st] = dy - bar_bot[st];
			bar_ng[st] = l - i && l - 1 - i	// number of gaps

			// define the helper lines
			if (stlines[l-1]!= '.') {	// if any staff line
				staff_tb[st].hll = 17 + i * 2	// pitch of lowest note
							// without helper line
							// ('D' when standard staff)
				if (i == l)
					staff_tb[st].hll -= 2
				staff_tb[st].hlmap =
					hlmap = new Int8Array((l - i + 1) * 2 + 2)
				for (j = 1; i < l; i++, j += 2) {
					switch (stlines[i]) {
					case '|':
					case '[':
					case "'":
//					case ':':
						hlmap[j - 1] = 1	// no helper line
						hlmap[j] = 1
						hlmap[j + 1] = 1
						break
					}
				}
			}

			dy = (cur_sy.staves[st].flags & STOP_BAR) ?
					0 : bar_bot[st]
		}

		// if in the middle of the tune, check the previous bar(s)
		i = ba.length
		if (!i)
			return
		while (--i >= 0) {
			b = ba[i]
			st = b[0].st
			if (b[1] > bar_bot[st])
				b[1] = bar_bot[st]
			if (b[2] < bar_height[st])
				b[2] = bar_height[st]
			if (b[3] < bar_ng[st])
				b[3] = bar_ng[st]
			if (b[0].seqst)		// end of time sequence
				break
		}
	} // bar_set()

	/* -- draw a staff -- */
	function draw_staff(st, x1, x2) {
	    var	w, i, dy, ty,
			y = 0,
			ln = "",
		tycl = {
		"|": "slW",		// normal
		"[": "slthW",		// thick
		"'": "sltnW",		// thin
		":": "sldW"		// dash
		},
		stafflines = cur_sy.staves[st].stafflines,
			l = stafflines.length,
			il = 6 * staff_tb[st].staffscale // interline

		if (!/[\[|':]/.test(stafflines))	// '
			return				// no line
		w = x2 - x1;
		set_sscale(-1)

		// check if default staff
		if (cache && cache.st_l == stafflines
		 && staff_tb[st].staffscale == 1
︙︙
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
						'h' + w.toFixed(1);
					dy = 0
					continue
				}
				if (ty != undefined)
					ln += '"/>\n';
				ty = stafflines[i]
				ln += '<path class="' +
					(ty == '[' ? 'slthW' : 'slW') +
					'" d="m0 ' + y + 'h' + w.toFixed(1);
				dy = 0
			}
			ln += '"/>'
		}
		y = staff_tb[st].y
		if (!cache







|
<







3545
3546
3547
3548
3549
3550
3551
3552

3553
3554
3555
3556
3557
3558
3559
						'h' + w.toFixed(1);
					dy = 0
					continue
				}
				if (ty != undefined)
					ln += '"/>\n';
				ty = stafflines[i]
				ln += '<path class="' + tycl[ty] +

					'" d="m0 ' + y + 'h' + w.toFixed(1);
				dy = 0
			}
			ln += '"/>'
		}
		y = staff_tb[st].y
		if (!cache
︙︙
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497

3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
			xygl(x1, y, i)
			return
		}
		out_XYAB('<g transform="translate(X, Y)">\n' + ln + '\n</g>\n', x1, y)
	} // draw_staff()

	// draw a measure bar
	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

		// don't put a line between the staves if there is no bar above
		if (st != 0
		 && s.ts_prev
//fixme: 's.ts_prev.st != st - 1' when floating voice in lower staff
//	 && (s.ts_prev.type != C.BAR || s.ts_prev.st != st - 1))
		 && 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);

		// compute the middle vertical offset of the staff
		yb = p_staff.y + 12;
		if (p_staff.stafflines != '|||||')
			yb += (p_staff.topbar + p_staff.botbar) / 2 - 12 // bottom

		// if measure repeat, draw the '%' like glyphs
		if (s.bar_mrep) {
			set_sscale(st)
			if (s.bar_mrep == 1) {
				for (s2 = s.prev; s2.type != C.REST; s2 = s2.prev)
					;







|




>








|









|
<
<







3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602


3603
3604
3605
3606
3607
3608
3609
			xygl(x1, y, i)
			return
		}
		out_XYAB('<g transform="translate(X, Y)">\n' + ln + '\n</g>\n', x1, y)
	} // draw_staff()

	// draw a measure bar
	function draw_bar(s, bot, h, ng) {
	    var	i, s2, yb, w,
		bar_type = s.bar_type,
		st = s.st,
		p_staff = staff_tb[st],
		top = ng >= 3 ? 6 * ng : ng == 1 ? 18 : 12,
		x = s.x

		// don't put a line between the staves if there is no bar above
		if (st != 0
		 && s.ts_prev
//fixme: 's.ts_prev.st != st - 1' when floating voice in lower staff
//	 && (s.ts_prev.type != C.BAR || s.ts_prev.st != st - 1))
		 && s.ts_prev.type != C.BAR)
			h = top * p_staff.staffscale

		s.ymx = s.ymn + h;

		set_sscale(-1)
		anno_start(s)
		if (s.color)
			set_color(s.color);

		// compute the middle vertical offset of the staff
		yb = bot + top / 2



		// if measure repeat, draw the '%' like glyphs
		if (s.bar_mrep) {
			set_sscale(st)
			if (s.bar_mrep == 1) {
				for (s2 = s.prev; s2.type != C.REST; s2 = s2.prev)
					;
︙︙
3535
3536
3537
3538
3539
3540
3541
3542



3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569




3570

3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610

3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628


3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647


3648
3649
3650
3651
3652
3653
3654
		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(
			'<path class="bW" stroke-dasharray="A,A" d="MX Yv-G"/>\n',
						x, bot, w, h)
				} else if (s.color) {
					out_XYAB('<path class="bW" d="MX Yv-F"/>\n',
						x, bot, h)
				} else {
					sb += 'M' + sx(x).toFixed(1)
						+ ' ' + self.sy(bot).toFixed(1)
						+ 'v-' + h.toFixed(1)
				}
				break
			default:
//			case "[":
//			case "]":
				x -= 3;
				if (s.color)
					out_XYAB('<path class="bthW" d="MX Yv-F"/>\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)
	} // draw_bar()

	// output all the bars
	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 < l; i++) {
			b = ba[i];		// symbol, bottom, height
			draw_bar(b[0], b[1], b[2])
		}
		if (bx)
			gene.curfont.box = bx

		set_sscale(-1)
		if (sb)			// single bars
			output += '<path class="bW" d="'
				+ sb
				+ '"/>\n'

		if (thb)		// thick bars [x, y, h]
			output += '<path class="bthW" d="'
				+ thb
				+ '"/>\n'
	} // out_bars()

	// set the helper lines of rests
	function hl_rest(s) {
	    var	j,

		p_st = staff_tb[s.st],
		i = 5 - s.nflags,		// rest_tb index (5 = C_XFLAGS)
		x = s.x,
		y = s.y

		if (i < 6)	// no ledger line if rest smaller than minim
			return

		if (i == 7 && y == 12
		 && p_st.stafflines.length <= 2)
			y -= 6			// semibreve a bit lower

		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) {		// longa
				y -= 6
				j--
			}
			break
		case 7:				// semibreve
			y += 6
			j++
		case 6:				// minim
			break
		}
		switch (p_st.stafflines[j]) {
		case '|':
		case '[':


			break
		default:
			set_hl(p_st, j, x, -7, 7)
			break
		}
	} // hl_rest()








|
>
>
>

|
|

|
|



|







|
|



|




>
>
>
>
|
>



















|
|



















>









|





|


>
>
















|


>
>







3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
		if (bar_type == '||:')
			bar_type = '[|:'

		for (i = bar_type.length; --i >= 0; ) {
			switch (bar_type[i]) {
			case "|":
				if (s.bar_dotted) {
					w = top / 6 <= 9
					    ? [0, 0, 4, 3.6, 4.8, 4.3, 4, 4.7, 4.4, 4.9]
							[top / 6]
						: 5
					out_XYAB(
			'<path class="bW" stroke-dasharray="A,A" d="MX YvG"/>\n',
						x, bot, w * p_staff.staffscale, -h)
				} else if (s.color) {
					out_XYAB('<path class="bW" d="MX YvF"/>\n',
						x, bot, -h)
				} else {
					sb += 'M' + sx(x).toFixed(1)
						+ ' ' + self.sy(bot).toFixed(1)
						+ 'v' + (-h).toFixed(1)
				}
				break
			default:
//			case "[":
//			case "]":
				x -= 3;
				if (s.color)
					out_XYAB('<path class="bthW" d="MX YvF"/>\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);
				if (ng & 1) {
					xygl(x, yb + 6, "rdot")
					xygl(x, yb - 6, "rdot")
				} else {
					xygl(x, yb - 12, "rdots")
				}
				set_sscale(-1)
				break
			}
			x -= 3
		}
		set_color();
		anno_stop(s)
	} // draw_bar()

	// output all the bars
	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 < l; i++) {
			b = ba[i]		// symbol, bottom, height, top
			draw_bar(b[0], b[1], b[2], b[3])
		}
		if (bx)
			gene.curfont.box = bx

		set_sscale(-1)
		if (sb)			// single bars
			output += '<path class="bW" d="'
				+ sb
				+ '"/>\n'

		if (thb)		// thick bars [x, y, h]
			output += '<path class="bthW" d="'
				+ thb
				+ '"/>\n'
	} // out_bars()

	// set the helper lines of rests
	function hl_rest(s) {
	    var	j,
		stlines = cur_sy.staves[s.st].stafflines,
		p_st = staff_tb[s.st],
		i = 5 - s.nflags,		// rest_tb index (5 = C_XFLAGS)
		x = s.x,
		y = s.y

		if (i < 6)	// no ledger line if rest smaller than minim
			return

		if (i == 7 && y == 12
		 && stlines.length <= 2)
			y -= 6			// semibreve a bit lower

		j = y / 6
		switch (i) {
		default:
			switch (stlines[j + 1]) {
			case '|':
			case '[':
			case "'":
			case ':':
				break
			default:
				set_hl(p_st, j + 1, x, -7, 7)
				break
			}
			if (i == 9) {		// longa
				y -= 6
				j--
			}
			break
		case 7:				// semibreve
			y += 6
			j++
		case 6:				// minim
			break
		}
		switch (stlines[j]) {
		case '|':
		case '[':
		case "'":
		case ':':
			break
		default:
			set_hl(p_st, j, x, -7, 7)
			break
		}
	} // hl_rest()

︙︙
3693
3694
3695
3696
3697
3698
3699



3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
				if (sy.st_print[st]	// if not staff stop
				 && 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:		// display the bars after the staves
			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			// if not at end of line
				 && s.next.type == C.CLEF) {	// and before a clef







>
>
>



















|







3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
				if (sy.st_print[st]	// if not staff stop
				 && 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
					if (sy.staves[st].stafflines.length >
					    cur_sy.staves[st].stafflines.length)
						x2 -= s.ts_prev.wl - 4
				} 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:		// display the bars after the staves
			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], bar_ng[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			// if not at end of line
				 && s.next.type == C.CLEF) {	// and before a clef
︙︙
Changes to core/format.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - format.js - formatting functions
//
// Copyright (C) 2014-2023 Jean-Francois Moine
//
// This file is part of abc2svg-core.
//
// abc2svg-core is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.


|







1
2
3
4
5
6
7
8
9
10
// abc2svg - format.js - formatting functions
//
// Copyright (C) 2014-2024 Jean-Francois Moine
//
// This file is part of abc2svg-core.
//
// abc2svg-core is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
︙︙
22
23
24
25
26
27
28

29
30
31
32
33
34
35
		serifBold: 1,
		'sans-serif': 1,
		'sans-serifBold': 1,
		Palatino: 1.1,
		monospace: 1
	},
	txt_ff = "text,serif",		// text font-family (serif for compatibility)

	fmt_lock = {}

var cfmt = {
	"abc-version": "1",		// default: old version
	annotationfont: {name: "text,sans-serif", size: 12},
	aligncomposer: 1,
	beamslope: .4,			// max slope of a beam







>







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
		serifBold: 1,
		'sans-serif': 1,
		'sans-serifBold': 1,
		Palatino: 1.1,
		monospace: 1
	},
	txt_ff = "text,serif",		// text font-family (serif for compatibility)
	ff = {},			// font-face's from %%beginsvg
	fmt_lock = {}

var cfmt = {
	"abc-version": "1",		// default: old version
	annotationfont: {name: "text,sans-serif", size: 12},
	aligncomposer: 1,
	beamslope: .4,			// max slope of a beam
︙︙
105
106
107
108
109
110
111
112

113
114
115
116
117
118
119
	staffsep: 46,
	stemheight: 21,			// one octave
	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},
//	textoption: undefined,
	textspace: 14,
	tieheight: 1.0,
	titlefont: {name: txt_ff, size: 20},
//	titleleft: false,







|
>







106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
	staffsep: 46,
	stemheight: 21,			// one octave
	stretchlast: .25,
	stretchstaff: true,
	subtitlefont: {name: txt_ff, size: 16},
	subtitlespace: 3,
	sysstaffsep: 34,
	systnames: -1,			// (for compatibility)
	systvoices: 3,
	tempofont: {name: txt_ff, weight: "bold", size: 12},
	textfont: {name: txt_ff, size: 16},
//	textoption: undefined,
	textspace: 14,
	tieheight: 1.0,
	titlefont: {name: txt_ff, size: 20},
//	titleleft: false,
︙︙
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
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, //true
vocalspace: true







|







154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
measrepnb: true,
rbmax: true,
rbmin: true,
shiftunison: true,
slurheight: true,
squarebreve: true,    
staffsep: true,
systvoices: 1, //true
stemheight: true,
stretchlast: true,
stretchstaff: true,
tieheight: true,
timewarn: true,
trimsvg: 1, //true
vocalspace: true
︙︙
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409

410


411
412
413
414
415
416
417
	case "px":		// screen pixel in 1/96 inch
	case "":
		return v[1]
	}
	return NaN
}

// set the infoname
function set_infoname(param) {
//fixme: check syntax: '<letter> ["string"]'
	var	tmp = cfmt.infoname.split("\n"),
		letter = param[0]

	for (var i = 0; i < tmp.length; i++) {
		var infoname = tmp[i]
		if (infoname[0] != letter)
			continue
		if (param.length == 1)
			tmp.splice(i, 1)
		else
			tmp[i] = param
		cfmt.infoname = tmp.join('\n')
		return
	}

	cfmt.infoname += "\n" + param


}

// get the text option
var textopt = {
	align: 'j',
	center: 'c',
	fill: 'f',







|
|

|










|


>
|
>
>







388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
	case "px":		// screen pixel in 1/96 inch
	case "":
		return v[1]
	}
	return NaN
}

// set the name of an info or a part
function set_infoname(cmd, param) {
//fixme: check syntax: '<letter> ["string"]'
    var	tmp = cfmt[cmd] ? cfmt[cmd].split("\n") : "",
		letter = param[0]

	for (var i = 0; i < tmp.length; i++) {
		var infoname = tmp[i]
		if (infoname[0] != letter)
			continue
		if (param.length == 1)
			tmp.splice(i, 1)
		else
			tmp[i] = param
		cfmt[cmd] = tmp.join('\n')
		return
	}
	if (cfmt[cmd])
		cfmt[cmd] += "\n" + param
	else
		cfmt[cmd] = param
}

// get the text option
var textopt = {
	align: 'j',
	center: 'c',
	fill: 'f',
︙︙
530
531
532
533
534
535
536

537
538
539
540








541
542
543
544
545
546
547
	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":







>




>
>
>
>
>
>
>
>







535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
	case "infoline":
	case "measurenb":
	case "rbmax":
	case "rbmin":
	case "measrepnb":
	case "shiftunison":
	case "systnames":
	case "systvoices":
		v = parseInt(param)
		if (isNaN(v)) {
			syntax(1, "Bad integer value");
			break
		}
		if (cmd == "systnames") {	// compatibility
			switch (v) {
			case -1: v = 3; break
			case 1: v = 2; break
			case 2: v = 1; break
			}
			cmd = "systvoices"
		}
		cfmt[cmd] = v
		break
	case "abc-version":
	case "bgcolor":
	case "fgcolor":
	case "propagate-accidentals":
︙︙
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
	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":







<







607
608
609
610
611
612
613

614
615
616
617
618
619
620
	case "decoerr":
	case "flatbeams":
	case "graceslurs":
	case "graceword":
	case "hyphencont":
	case "keywarn":
	case "linewarn":

	case "squarebreve":
	case "splittune":
	case "straightflags":
	case "stretchstaff":
	case "timewarn":
	case "titlecaps":
	case "titleleft":
︙︙
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
	case "sysstaffsep":
	case "textspace":
	case "titlespace":
	case "topspace":
	case "vocalspace":
	case "wordsspace":
		f = get_unit(param)	// normally, unit in points - 72 DPI accepted
		if (isNaN(f))
			syntax(1, errs.bad_val, '%%' + cmd)
		else
			cfmt[cmd] = f
		break
	case "page-format":
		user.page_format = get_bool(param)
		break







|







653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
	case "sysstaffsep":
	case "textspace":
	case "titlespace":
	case "topspace":
	case "vocalspace":
	case "wordsspace":
		f = get_unit(param)	// normally, unit in points - 72 DPI accepted
		if (isNaN(f) || f < 0)
			syntax(1, errs.bad_val, '%%' + cmd)
		else
			cfmt[cmd] = f
		break
	case "page-format":
		user.page_format = get_bool(param)
		break
︙︙
721
722
723
724
725
726
727

728
729
730
731
732
733
734
735
			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







>
|







734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
			v[3] = f
		if (curvoice)
			curvoice.tup = v
		else
			cfmt[cmd] = v
		break
	case "infoname":
	case "partname":
		set_infoname(cmd, param)
		break
	case "notespacingfactor":
		v = param.match(/([.\d]+)[,\s]*(\d+)?/)
		if (v) {
			f = +v[1]
			if (isNaN(f) || f < 1 || f > 2) {
				f = 0
︙︙
817
818
819
820
821
822
823

824
825
826
827
828
829
830
831
832
833
834
835
836
		cfmt.rightmargin = v;
		img.chg = true
		break
	case "textoption":
		cfmt[cmd] = get_textopt(param)
		break
	case "dynalign":

	case "singleline":
	case "stretchlast":
	case "titletrim":
		v = +param
		if (isNaN(v))
			v = get_bool(param) ? 0 : 1
		if (cmd[1] == 't') {		// stretchlast
			if (v < 0 || v > 1) {
				syntax(1, errs.bad_val, '%%' + cmd)
				break
			}
		}
		cfmt[cmd] = v







>



|

|







831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
		cfmt.rightmargin = v;
		img.chg = true
		break
	case "textoption":
		cfmt[cmd] = get_textopt(param)
		break
	case "dynalign":
	case "quiet":
	case "singleline":
	case "stretchlast":
	case "titletrim":
		v = param == '' ? 1 : +param
		if (isNaN(v))
			v = +get_bool(param)
		if (cmd[1] == 't') {		// stretchlast
			if (v < 0 || v > 1) {
				syntax(1, errs.bad_val, '%%' + cmd)
				break
			}
		}
		cfmt[cmd] = v
︙︙
908
909
910
911
912
913
914
915

916

917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934




935
936
937
938
939
940
941
		if (font.fid == undefined) {	// if default font
			font.fid = abc2svg.font_tb.length
			abc2svg.font_tb.push(font)
			if (!font.swfac)
				set_font_fac(font)
			if (!font.pad)
				font.pad = 0


			// set the pointer to the width of the characters

			font.cw_tb = !font.name ? ssw_tb
				: font.name.indexOf("ans") > 0
					? ssw_tb		// sans-serif
					: font.name.indexOf("ono") > 0
						? mw_tb		// monospace
						: sw_tb		// serif
		}
		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 more music font style
			add_fstyle(".f" + font.fid
				+ (cfmt.fullsvg || "")
				+ ' text,tspan{white-space:pre}')




	}
}

// get the font of the 'xxxfont' parameter
function get_font(fn) {
    var	font, font2, fid, st








|
>

>


















>
>
>
>







923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
		if (font.fid == undefined) {	// if default font
			font.fid = abc2svg.font_tb.length
			abc2svg.font_tb.push(font)
			if (!font.swfac)
				set_font_fac(font)
			if (!font.pad)
				font.pad = 0
		}

			// set the pointer to the width of the characters
		if (!font.cw_tb) {
			font.cw_tb = !font.name ? ssw_tb
				: font.name.indexOf("ans") > 0
					? ssw_tb		// sans-serif
					: font.name.indexOf("ono") > 0
						? mw_tb		// monospace
						: sw_tb		// serif
		}
		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 more music font style
			add_fstyle(".f" + font.fid
				+ (cfmt.fullsvg || "")
				+ ' text,tspan{white-space:pre}')
		if (ff.text && !ff.used && font.name.indexOf("text") >= 0) {
			font_style += ff.text	// add font-face's from %%beginsvg
			ff.used = 1 //true
		}
	}
}

// get the font of the 'xxxfont' parameter
function get_font(fn) {
    var	font, font2, fid, st

︙︙
951
952
953
954
955
956
957
958

959
960
961
962
963
964
965
966
967
968
969
970
		if (font.name)
			font2.name = font.name
		if (font.normal) {
			if (font2.weight)	// !! don't use delete !!
				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







<
>




<







972
973
974
975
976
977
978

979
980
981
982
983

984
985
986
987
988
989
990
		if (font.name)
			font2.name = font.name
		if (font.normal) {
			if (font2.weight)	// !! don't use delete !!
				font2.weight = null
			if (font2.style)
				font2.style = null

		}
			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
︙︙
Changes to core/front.js.
︙︙
213
214
215
216
217
218
219

220
221
222
223
224
225
226
		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--
}

// parse ABC code
function tosvg(in_fname,		// file name
		file,			// file content







>







213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
		syntax(1, "Cannot read file '$1'", fn)
		return
	}
	include++;
	parse_sav = clone(parse);
	tosvg(fn, file);
	parse_sav.state = parse.state;
	parse_sav.ckey = parse.ckey
	parse = parse_sav;
	include--
}

// parse ABC code
function tosvg(in_fname,		// file name
		file,			// file content
︙︙
267
268
269
270
271
272
273
274




































275

276
277
278
279
280
281
282
283
284
285
286
287
288
289
290





291
292
293
294
295
296
297
			src = src.replace(/([^\\])%.*/, '$1')
				 .replace(/\\%/g, '%');
		src = src.replace(/\s+$/, '')
		if (flag && src.indexOf('\\') >= 0)
			return cnv_escape(src, flag)
		return src
	} // uncomment()





































	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.pq
		init_tune()
		img.chg = true;
		set_page();





	} // end_tune()

	// get %%voice
	function do_voice(select, in_tune) {
	    var	opt, bol
		if (select == "end")
			return		// end of previous %%voice








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>















>
>
>
>
>







268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
			src = src.replace(/([^\\])%.*/, '$1')
				 .replace(/\\%/g, '%');
		src = src.replace(/\s+$/, '')
		if (flag && src.indexOf('\\') >= 0)
			return cnv_escape(src, flag)
		return src
	} // uncomment()

	// set the sequence showing the source and save it in sav.src
	function set_src(stag, se) {
	    var	r, t,
		etag = ""

		if (!se)
			se = file.indexOf('\n\n', bol)	// end of tune
		if (se < 0)
			se = eof
		if (typeof stag != "object") {		// set the tag after source
			if (stag[0] != 'b' && stag[0] != 'a' && stag[0] != '+'
			 && stag[0] != '*')
				stag = 'b' + stag	// default: source before
			if (stag[1] != '<')		// (if bool)
				stag = stag[0] + "<pre>"
			r = stag.match(/<\/?[^>]*>/g)
			while (1) {
				t = r.pop()
				if (!t)
					break
				if (t[1] == '/' || t.slice(-2) == '/>')
					r.pop()		// skip this stop/start tag
				else
					etag += '</' + t.slice(1)
			}
			cfmt.show_source = stag = [stag, etag]
		}
		t = stag[0].slice(1)
			+ clean_txt(file.slice(bol, se))
			+ stag[1]
		if (stag[0][0] == '+' && sav.src)
			sav.src += t
		else
			sav.src = t
	} // set_src()

	function end_tune() {
		parse.bol = bol				// (for multi V:)
		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.pq
		init_tune()
		img.chg = true;
		set_page();
		if (cfmt.show_source) {
			user.img_out("</div>")
			if (cfmt.show_source[0][0] == 'a')
				user.img_out(sav.src)
		}
	} // end_tune()

	// get %%voice
	function do_voice(select, in_tune) {
	    var	opt, bol
		if (select == "end")
			return		// end of previous %%voice
︙︙
496
497
498
499
500
501
502


















503
504
505
506
507
508
509
510
511
512
513
514
515
						.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, '\\(');







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





|







539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
						.replace(/^%%/gm,''))
				parse.eol = file.indexOf('\n', i + 6)
				if (parse.eol < 0)
					parse.eol = eof
				continue
			}
			switch (a[1]) {
			case "show_source":
				b = uncomment(a[2])
				switch (b[0]) {
				case '*':
					i = file.indexOf('\n' + line0 + line1
							+ "show_source", eol)
					bol = parse.eol
					set_src(b, i)
					user.img_out(sav.src)
					// fall thru
				case '0':
					b = ""
					// fall thru
				default:
					cfmt[a[1]] = b
					// fall thru
				}
				continue
			case "select":
				if (parse.state != 0) {
					syntax(1, errs.not_in_tune, "%%select")
					continue
				}
				select = uncomment(a[2])
				if (select[0] == '"')
					select = select.slice(1, -1);
				if (!select) {
					delete parse.select
					continue
				}
				select = select.replace(/\(/g, '\\(');
︙︙
639
640
641
642
643
644
645







646
647
648
649
650
651
652
653
654
655
656
657
			sav.cfmt = clone(cfmt);
			sav.info = clone(info, 2)	// (level 2 for info.V[])
			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			// tune header
			if (user.page_format
			 && blkdiv < 1)		// (if no newpage)
				blkdiv = 1	// the tune starts by the next SVG
			if (parse.tune_opts)
				tune_filter()
			continue
		case 'T':
			switch (parse.state) {
			case 0:
				continue







>
>
>
>
>
>
>


<
<
<







700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715



716
717
718
719
720
721
722
			sav.cfmt = clone(cfmt);
			sav.info = clone(info, 2)	// (level 2 for info.V[])
			sav.char_tb = clone(char_tb);
			sav.glovar = clone(glovar);
			sav.maps = clone(maps, 1);
			sav.mac = clone(mac);
			sav.maci = clone(maci);
			if (cfmt.show_source) {
				bol -= 2
				set_src(cfmt.show_source)
				if (cfmt.show_source[0][0] == 'b')
					user.img_out(sav.src)
				user.img_out('<div class="source">')
			}
			info.X = text;
			parse.state = 1			// tune header



			if (parse.tune_opts)
				tune_filter()
			continue
		case 'T':
			switch (parse.state) {
			case 0:
				continue
︙︙
745
746
747
748
749
750
751




752
753
754
		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







>
>
>
>



810
811
812
813
814
815
816
817
818
819
820
821
822
823
		return
	if (parse.state == 1) {
		syntax(1, "End of file in tune header")
		get_key("C")
	}
	if (parse.state >= 2)
		end_tune();
	if (sav.src && cfmt.show_source[0] == '+') {
		user.img_out(sav.src)		// source of all tunes
		sav.src = null
	}
	parse.state = 0
}
Abc.prototype.tosvg = tosvg
Changes to core/lyrics.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - lyrics.js - lyrics
//
// Copyright (C) 2014-2023 Jean-Francois Moine
//
// This file is part of abc2svg-core.
//
// abc2svg-core is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.


|







1
2
3
4
5
6
7
8
9
10
// abc2svg - lyrics.js - lyrics
//
// Copyright (C) 2014-2024 Jean-Francois Moine
//
// This file is part of abc2svg-core.
//
// abc2svg-core is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
︙︙
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137






138
139
140
141
142
143
144
		s = s.next;
		i++
	}
	curvoice.sym_cont = s
}

/* -- parse a lyric (vocal) line (w:) -- */
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

	// get the starting symbol of the lyrics
	if (cont) {					// +:
		s = curvoice.lyric_cont
		if (!s) {
			syntax(1, "+: lyric without music")
			return






		}
		dfnt = get_font("vocal")
		if (gene.deffont != dfnt) {	// if vocalfont change
			if (gene.curfont == gene.deffont)
				gene.curfont = dfnt
			gene.deffont = dfnt
		}







|
|












>
>
>
>
>
>







117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
		s = s.next;
		i++
	}
	curvoice.sym_cont = s
}

/* -- parse a lyric (vocal) line (w:) -- */
function get_lyrics(p, cont) {
    var s, word, i, j, ly, dfnt, ln, c, cf

	if (curvoice.ignore)
		return
	if ((curvoice.pos.voc & 0x07) != C.SL_HIDDEN)
		curvoice.have_ly = true

	// get the starting symbol of the lyrics
	if (cont) {					// +:
		s = curvoice.lyric_cont
		if (!s) {
			syntax(1, "+: lyric without music")
			return
		}
		if (p[0] == '~') {			// +:~next~words
			while (!s.a_ly)
				s = s.prev
			ly = s.a_ly[curvoice.lyric_line]
			p = ly.t.replace(/ /g,'~') + p
		}
		dfnt = get_font("vocal")
		if (gene.deffont != dfnt) {	// if vocalfont change
			if (gene.curfont == gene.deffont)
				gene.curfont = dfnt
			gene.deffont = dfnt
		}
︙︙
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
		if (!s) {
			syntax(1, "w: without music")
			return
		}
	}

	/* scan the lyric line */
	p = text;
	i = 0
	cf = gene.curfont
	while (1) {
		while (p[i] == ' ' || p[i] == '\t')
			i++
		if (!p[i])
			break







<







163
164
165
166
167
168
169

170
171
172
173
174
175
176
		if (!s) {
			syntax(1, "w: without music")
			return
		}
	}

	/* scan the lyric line */

	i = 0
	cf = gene.curfont
	while (1) {
		while (p[i] == ' ' || p[i] == '\t')
			i++
		if (!p[i])
			break
︙︙
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
	if (s3.wl < wl) {
		s3.shrink += wl - s3.wl
		s3.wl = wl
	}

	// if not room enough, shift the following notes to the right
	dx -= 6
	if (dx < wx) {
		dx = (wx - dx) / n
		s1 = s.ts_next
		while (1) {
			if (s1.shrink) {
				s1.shrink += dx
				s3.wr += dx	// (needed for end of line)
				s3 = s1







|







360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
	if (s3.wl < wl) {
		s3.shrink += wl - s3.wl
		s3.wl = wl
	}

	// if not room enough, shift the following notes to the right
	dx -= 6
	if (dx < wx && s2) {
		dx = (wx - dx) / n
		s1 = s.ts_next
		while (1) {
			if (s1.shrink) {
				s1.shrink += dx
				s3.wr += dx	// (needed for end of line)
				s3 = s1
︙︙
Changes to core/modules.js.
︙︙
49
50
51
52
53
54
55

56
57
58
59
60
61
62
	pedline: {},
	percmap: { fn: 'perc' },
	roman: {},
	soloffs: {},
	sth: {},
	strtab: {},
	temperament: { fn: 'temper' },

	tropt: {},

	nreq: 0,

	// scan the file and find the required modules
	// @file: ABC file
	// @relay: (optional) callback function for continuing the treatment







>







49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
	pedline: {},
	percmap: { fn: 'perc' },
	roman: {},
	soloffs: {},
	sth: {},
	strtab: {},
	temperament: { fn: 'temper' },
	temponame: { fn: 'tempo' },
	tropt: {},

	nreq: 0,

	// scan the file and find the required modules
	// @file: ABC file
	// @relay: (optional) callback function for continuing the treatment
︙︙
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
			if (--abc2svg.modules.nreq == 0)
				abc2svg.modules.cbf()
		}

		// test if some keyword in the file
	    var	m, i, fn,
		nreq_i = this.nreq,
		ls = file.match(/(^|\n)(%%|I:).+?\b/g)

		if (!ls)
			return true
		this.cbf = relay ||		// (only one callback function)
			function(){}
		this.errmsg = errmsg || get_errmsg()








|







83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
			if (--abc2svg.modules.nreq == 0)
				abc2svg.modules.cbf()
		}

		// test if some keyword in the file
	    var	m, i, fn,
		nreq_i = this.nreq,
		ls = file.match(/(%%|I:).+?\b/g)

		if (!ls)
			return true
		this.cbf = relay ||		// (only one callback function)
			function(){}
		this.errmsg = errmsg || get_errmsg()

︙︙
Changes to core/music.js.
︙︙
63
64
65
66
67
68
69




70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
	5,		// OVAL
	6,		// OVALBARS
	7		// SQUARE
])

// get head type, dots, flags of note/rest for a duration
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			/* see C.BLEN for values */
	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) {







>
>
>
>














<
<
<
<







63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87




88
89
90
91
92
93
94
	5,		// OVAL
	6,		// OVALBARS
	7		// SQUARE
])

// get head type, dots, flags of note/rest for a duration
function identify_note(s, dur_o) {
    var	r = abc2svg.hdn[dur_o]
	if (r)				// in cache?
		return r

    var	head, flags,
	dots = 0,
	dur = dur_o

	if (dur % 12 != 0)
		error(1, s, "Invalid note duration $1", dur);
	dur /= 12			/* see C.BLEN for values */
	if (!dur)
		error(1, s, "Note too short")
	for (flags = 5; dur; dur >>= 1, flags--) {
		if (dur & 1)
			break
	}
	dur >>= 1




	while (dur >> dots > 0)
		dots++

	flags -= dots
	if (flags >= 0) {
		head = C.FULL
	} else switch (flags) {
︙︙
105
106
107
108
109
110
111

112

113
114
115
116
117
118
119
	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







>
|
>







105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
	case -2:
		head = C.OVAL
		break
	case -1:
		head = C.EMPTY
		break
	}
	abc2svg.hdn[dur_o] =
		r = [head, dots, flags]
	return r
}

function set_head_shift(s) {
	var	i, i1, i2, d, ps, dx,
		dx_head = dx_tb[s.head],
		dir = s.stem,
		n = s.nhd
︙︙
863
864
865
866
867
868
869



870
871
872
873
874
875
876
						w += 1
						// fall thru
					case ":":
						w += 2
						break
					}
				}



				break
			}
			s.wl = w
			if (s.next
			 && s.next.type != C.METER)
				s.wr = 7
			else







>
>
>







865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
						w += 1
						// fall thru
					case ":":
						w += 2
						break
					}
				}
				if (bar_type[0] == ":"		// if "c3 :|"
				 && s.prev && s.prev.dots)
					w += 4
				break
			}
			s.wl = w
			if (s.next
			 && s.next.type != C.METER)
				s.wr = 7
			else
︙︙
899
900
901
902
903
904
905

906
907
908
909
910
911
912
913
914
915
916
			s.wl = deco_width(s, s.wl)

		/* have room for the repeat numbers / chord indication */
		if (s.text && s.text.length < 4
		 && s.next && s.next.a_gch) {
			set_font("repeat");
			s.wr += strwh(s.text)[0] + 2

			if (cfmt.measurenb > 0 & s.bar_num
			 && s.bar_num % cfmt.measurenb)
				s.wr += 4
		}
		return
	case C.CLEF:
// (there may be invisible clefs in empty staves)
		if (s.invis) {
			s.wl = s.wr = 1		// (!! not 0 !!)
			return
		}







>
|
|

<







904
905
906
907
908
909
910
911
912
913
914

915
916
917
918
919
920
921
			s.wl = deco_width(s, s.wl)

		/* have room for the repeat numbers / chord indication */
		if (s.text && s.text.length < 4
		 && s.next && s.next.a_gch) {
			set_font("repeat");
			s.wr += strwh(s.text)[0] + 2
		}
		if (cfmt.measurenb > 0 && s.bar_num
			 && !(s.bar_num % cfmt.measurenb))
				s.wr += 4

		return
	case C.CLEF:
// (there may be invisible clefs in empty staves)
		if (s.invis) {
			s.wl = s.wr = 1		// (!! not 0 !!)
			return
		}
︙︙
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005

1006
1007



1008
1009



1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
		wlw = 0
		for (i = 0; i < s.a_meter.length; i++) {
			meter = s.a_meter[i]
			switch (meter.top[0]) {
			case 'C':
			case 'c':
			case 'o':
				s.x_meter[i] = wlw + 6;
				wlw += 12
				break
			case '.':
			case '|':
				s.x_meter[i] = s.x_meter[i - 1]
				break
			default:
				w = 0
				if (!meter.bot
				 || meter.top.length > meter.bot.length)
					meter = meter.top
				else
					meter = meter.bot;
				for (m = 0; m < meter.length; m++) {
					switch (meter[m]) {
					case '(':
						wlw += 4

						// fall thru
					case ')':



					case '1':
						w += 4



						break
					default:
						w += 12
						break
					}
				}
				s.x_meter[i] = wlw + w / 2
				wlw += w
			}
		}
		s.wl = 1
		s.wr = wlw + 7
		return
	case C.MREST:







|




|











|
>
|

>
>
>

|
>
>
>






|







986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
		wlw = 0
		for (i = 0; i < s.a_meter.length; i++) {
			meter = s.a_meter[i]
			switch (meter.top[0]) {
			case 'C':
			case 'c':
			case 'o':
				s.x_meter[i] = wlw
				wlw += 12
				break
			case '.':
			case '|':
				s.x_meter[i] = s.x_meter[i - 1] + 4.5
				break
			default:
				w = 0
				if (!meter.bot
				 || meter.top.length > meter.bot.length)
					meter = meter.top
				else
					meter = meter.bot;
				for (m = 0; m < meter.length; m++) {
					switch (meter[m]) {
					case '(':
						wlw += 2
						w += 6
						break
					case ')':
						wlw -= 2
						w += 6
						break
					case '1':
						w += 6
						break
					case ' ':
						w += 2
						break
					default:
						w += 12
						break
					}
				}
				s.x_meter[i] = wlw
				wlw += w
			}
		}
		s.wl = 1
		s.wr = wlw + 7
		return
	case C.MREST:
︙︙
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274


1275
1276
1277
1278
1279
1280
1281
	sn = s.ts_next		// start of next line

	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)			// (must not be the end of the voice)
		s.next.prev = b
//	if (s.ts_next)
		s.ts_next.ts_prev = b
	s.next = s.ts_next = b


	b.space = sn.space * .9 - 3
	return b
}

/* -- set the width and space of all symbols -- */
// this function is called once for the whole tune
// and once more for each new music line







<






>
>







1273
1274
1275
1276
1277
1278
1279

1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
	sn = s.ts_next		// start of next line

	b.wl = 0
	b.wr = 0
	b.ts_prev = s
	b.next = s.next
	b.ts_next = s.ts_next


	if (s.next)			// (must not be the end of the voice)
		s.next.prev = b
//	if (s.ts_next)
		s.ts_next.ts_prev = b
	s.next = s.ts_next = b
	b.shrink = sn.shrink
	sn.shrink = sn.wl + 10
	b.space = sn.space * .9 - 3
	return b
}

/* -- set the width and space of all symbols -- */
// this function is called once for the whole tune
// and once more for each new music line
︙︙
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326


1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340

1341
1342
1343
1344
1345
1346
1347
			if (s.prev && s.prev.st != st) {
				xl[st] = xl[s.prev.st]
				wr[st] = wr[s.prev.st]
			}
			val = xl[st] + wr[st] + s.wl
			if (val > maxx)
				maxx = val
			if (s.dur && s.dur != s.notes[0].dur	// if in tuplet
			 && first)			// (first time only)
				itup = 1
			s = s.ts_next
		} while (s && !s.seqst);

		// set the spaces of the time sequence
		s2.shrink = maxx - xa
		s2.space = s2.ts_prev ? set_space(s2, tim) : 0

		// adjust the spacing when after a spacer (y)
		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;		/* no space */
			s2.time = tim
		    } else {
			s2.shrink = 10			// cannot be null

		    }
		}
		tim = s2.time
		if (!s)
			break

		// update the min left space per staff







|
<














>
>













|
>







1317
1318
1319
1320
1321
1322
1323
1324

1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
			if (s.prev && s.prev.st != st) {
				xl[st] = xl[s.prev.st]
				wr[st] = wr[s.prev.st]
			}
			val = xl[st] + wr[st] + s.wl
			if (val > maxx)
				maxx = val
			if (s.dur && s.dur != s.notes[0].dur)	// if in tuplet

				itup = 1
			s = s.ts_next
		} while (s && !s.seqst);

		// set the spaces of the time sequence
		s2.shrink = maxx - xa
		s2.space = s2.ts_prev ? set_space(s2, tim) : 0

		// adjust the spacing when after a spacer (y)
		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 (!first)
				break
			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;		/* no space */
			s2.time = tim
		    } else {
			s2.shrink = s2.wl		// cannot be null
			maxx += s2.wl
		    }
		}
		tim = s2.time
		if (!s)
			break

		// update the min left space per staff
︙︙
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
	}

	if (stup)
		set_sp_tup(stup, s2)

	// let the chord symbols at the same offset
	// and adjust the spacing due to the lyrics
	if (first && s_chs)
		set_w_chs(s_chs)
}

// insert a rest, this one replacing a sequence or a measure
function to_rest(so) {
    var	s = clone(so)








|







1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
	}

	if (stup)
		set_sp_tup(stup, s2)

	// let the chord symbols at the same offset
	// and adjust the spacing due to the lyrics
	if (s_chs)
		set_w_chs(s_chs)
}

// insert a rest, this one replacing a sequence or a measure
function to_rest(so) {
    var	s = clone(so)

︙︙
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
	function do_warn(s) {		// start of next line
	    var s1, s2, s3, s4, w

		// advance in the next line
		for (s2 = s; s2 && s2.time == s.time; s2 = s2.ts_next) {
			switch (s2.type) {
			case C.KEY:
				if (!s.fmt.keywarn
				 || s2.invis)
					continue
				for (s1 = s.ts_prev; s1 ;s1 = s1.ts_prev) {
					if (s1.type != C.METER)
						break
				}
				// fall thru
			case C.METER:
				if (s2.type == C.METER) {
					if (!s.fmt.timewarn)
						continue
					s1 = s.ts_prev
				}
				// fall thru
			case C.CLEF:
				if (!s2.prev)		// start of voice
					continue
				if (s2.type == C.CLEF) {
					if (s2.invis)	// if 'K: clef=none' after bar
						break
					for (s1 = s.ts_prev; s1; s1 = s1.ts_prev) {
						switch (s1.type) {
						case C.BAR:
							if (s1.bar_type[0] == ':')
								break
							// fall thru







|


















|







1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
	function do_warn(s) {		// start of next line
	    var s1, s2, s3, s4, w

		// advance in the next line
		for (s2 = s; s2 && s2.time == s.time; s2 = s2.ts_next) {
			switch (s2.type) {
			case C.KEY:
				if (!s2.fmt.keywarn
				 || s2.invis)
					continue
				for (s1 = s.ts_prev; s1 ;s1 = s1.ts_prev) {
					if (s1.type != C.METER)
						break
				}
				// fall thru
			case C.METER:
				if (s2.type == C.METER) {
					if (!s.fmt.timewarn)
						continue
					s1 = s.ts_prev
				}
				// fall thru
			case C.CLEF:
				if (!s2.prev)		// start of voice
					continue
				if (s2.type == C.CLEF) {
					if (s2.clef_none) // if 'K: clef=none' after bar
						break
					for (s1 = s.ts_prev; s1; s1 = s1.ts_prev) {
						switch (s1.type) {
						case C.BAR:
							if (s1.bar_type[0] == ':')
								break
							// fall thru
︙︙
2025
2026
2027
2028
2029
2030
2031

























































2032
2033
2034
2035
2036
2037
2038
			next,		/* symbol of the next line / null */
			lwidth,		/* w - (clef & key sig) */
			indent) {	/* for start of tune */
    var	first, s2, s3, s4, s5, x, xmin, xmid, xmax, wwidth, shrink, space,
	nlines,
	last = next ? next.ts_prev : null,
	ws = get_width(s, next)		// 2 widths: nice and shrunk


























































	// take care of big key signatures at end of line
	if (s.fmt.keywarn && next
	 && next.type == C.KEY && !last.dur) {
		ws[0] += next.wr
		ws[1] += next.wr
	}







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
			next,		/* symbol of the next line / null */
			lwidth,		/* w - (clef & key sig) */
			indent) {	/* for start of tune */
    var	first, s2, s3, s4, s5, x, xmin, xmid, xmax, wwidth, shrink, space,
	nlines,
	last = next ? next.ts_prev : null,
	ws = get_width(s, next)		// 2 widths: nice and shrunk

	// split a big lyric word on two music lines
	function ly_split(s, wmax) {
	    var	i, wh,
		s2 = clone(s),
		p = s.a_ly[0].t,		// lyric word
		w = 0,
		j = 0

		gene.deffont = s.a_ly[0].font
		while (1) {
			i = p.indexOf(' ', j) + 1
			if (i <= 0)
				break
			wh = strwh(p.slice(j, i))
			w += wh[0]
			if (w > wmax)
				break
			j = i
		}
		s.a_ly[0].t = new String(p.slice(0, j - 1))
		s2.a_ly = clone(s.a_ly)
		s2.a_ly[0] = clone(s.a_ly[0])
		s2.a_ly[0].t = new String(p.slice(j))
		if (abc2svg.el) {
			strwh(s.a_ly[0].t)
			strwh(s2.a_ly[0].t)
		} else {
			s.a_ly[0].t.wh = strwh(s.a_ly[0].t)
			s2.a_ly[0].t.wh = strwh(s2.a_ly[0].t)
		}
		w = s.a_ly[0].t.wh[0]		// new length of the words
		s.wr = wmax
		s2.wr -= w

		lkvsym(s2, s2.next)		// voice linkage

		s2.time += .1
//		s2.dur -= .1
		s2.nl = 0 //false
		while (!s.seqst)
			s = s.ts_prev
		s2.x = s.x + w
		s2.shrink = w - s2.wl
		s = s2.ts_next
		while (s) {
			if (s.seqst) {
				s.wl -= w
				s.shrink -= w
				break
			}
			s = s.ts_next
		}
		lktsym(s2, s2.ts_next)		// time linkage
	} // ly_split()

	// -- set_lines --

	// take care of big key signatures at end of line
	if (s.fmt.keywarn && next
	 && next.type == C.KEY && !last.dur) {
		ws[0] += next.wr
		ws[1] += next.wr
	}
︙︙
2070
2071
2072
2073
2074
2075
2076











2077
2078
2079
2080
2081
2082
2083
		s4 = s			// keep first symbol with x greater than xmin
//fixme: can this occur?
		if (s == next) {
			if (s)
				s = set_nl(s)
			return s
		}












		/* try to cut on a measure bar */
		s3 = null
		for ( ; s != next; s = s.ts_next) {
			x = s.x
			if (!x)
				continue







>
>
>
>
>
>
>
>
>
>
>







2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
		s4 = s			// keep first symbol with x greater than xmin
//fixme: can this occur?
		if (s == next) {
			if (s)
				s = set_nl(s)
			return s
		}

		// if the width of the symbol is greater than
		//			 the remaining width in the staff
		// and if there are lyrics, split these lyrics on 2 staves
	    if (s.x > xmax
	     && s.prev.a_ly) {
		s3 = s = s.prev
		while (!s3.seqst)
			s3 = s3.ts_prev
		ly_split(s, xmax - s3.x)
	    } else {

		/* try to cut on a measure bar */
		s3 = null
		for ( ; s != next; s = s.ts_next) {
			x = s.x
			if (!x)
				continue
︙︙
2143
2144
2145
2146
2147
2148
2149

2150
2151
2152
2153
2154
2155
2156
						s3 = s
					break
				}
				s3 = s
			}
		}
		s = s3

		while (s.ts_next) {
			s = s.ts_next
			if (s.seqst)
				break
		}

		if (s.nl) {		/* already set here - advance */







>







2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
						s3 = s
					break
				}
				s3 = s
			}
		}
		s = s3
	    } // ly_split call
		while (s.ts_next) {
			s = s.ts_next
			if (s.seqst)
				break
		}

		if (s.nl) {		/* already set here - advance */
︙︙
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
    var	s2, i, mc,
	pg_sav = {			// save the page parameters
		leftmargin: cfmt.leftmargin,
		rightmargin: cfmt.rightmargin,
		pagewidth: cfmt.pagewidth,
		scale: cfmt.scale
	},
	indent = lsh[0] - lsh[1],	// extra width of the first line
	ckw = get_ck_width(),		// width of the starting symbols
	s = tsfirst

	lwidth -= lsh[1]		// width of the lines
	if (cfmt.indent && cfmt.indent > lsh[0])
		indent += cfmt.indent

	// adjust the line width according to the starting symbols
	lwidth -= ckw[0]
	indent += ckw[1]

	if (cfmt.custos && voice_tb.length == 1)







|



|
|







2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
    var	s2, i, mc,
	pg_sav = {			// save the page parameters
		leftmargin: cfmt.leftmargin,
		rightmargin: cfmt.rightmargin,
		pagewidth: cfmt.pagewidth,
		scale: cfmt.scale
	},
	indent = lsh[1] - lsh[2],	// extra width of the first line
	ckw = get_ck_width(),		// width of the starting symbols
	s = tsfirst

	lwidth -= lsh[2]		// width of the lines
	if (cfmt.indent && cfmt.indent > lsh[1])
		indent += cfmt.indent

	// adjust the line width according to the starting symbols
	lwidth -= ckw[0]
	indent += ckw[1]

	if (cfmt.custos && voice_tb.length == 1)
︙︙
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
	}
} // mrest_expand()

// set the clefs (treble or bass) in a 'auto clef' sequence
// return the starting clef type
function set_auto_clef(st, s_start, clef_type_start) {
    var	s, time, s2, s3,
	max = 12,					/* "F," */
	min = 20					/* "G" */

	/* get the max and min pitches in the sequence */
	for (s = s_start; s; s = s.ts_next) {
		if (s.type == C.STAVES && s != s_start)
			break
		if (s.st != st)
			continue







|
|







2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
	}
} // mrest_expand()

// set the clefs (treble or bass) in a 'auto clef' sequence
// return the starting clef type
function set_auto_clef(st, s_start, clef_type_start) {
    var	s, time, s2, s3,
	max = 14,				// "A,"
	min = 18				// "E"

	/* get the max and min pitches in the sequence */
	for (s = s_start; s; s = s.ts_next) {
		if (s.type == C.STAVES && s != s_start)
			break
		if (s.st != st)
			continue
︙︙
3143
3144
3145
3146
3147
3148
3149




3150






3151
3152
3153
3154
3155
3156
3157
				 || (s.a_gch && s.ts_next.a_gch)
				 || s.invis)
					break
				if (s.ts_next.a_dd)
					s.a_dd = s.ts_next.a_dd
				if (s.ts_next.a_gch)
					s.a_gch = s.ts_next.a_gch




				unlksym(s.ts_next)






				// fall thru
			case C.NOTE:
			case C.GRACE:
				break
			}

			st = s.st;







>
>
>
>
|
>
>
>
>
>
>







3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
				 || (s.a_gch && s.ts_next.a_gch)
				 || s.invis)
					break
				if (s.ts_next.a_dd)
					s.a_dd = s.ts_next.a_dd
				if (s.ts_next.a_gch)
					s.a_gch = s.ts_next.a_gch
				if (s.p_v.scale != 1
				 && s.ts_next.p_v.scale > s.p_v.scale)
				 	unlksym(s)
				else
					unlksym(s.ts_next)

				if ((!s.ts_prev.dur || s.ts_prev.time != s.time
				  || s.ts_prev.st != s.st)
				 && (s.ts_next.time != s.time
				  || s.ts_next.st != s.st))
					continue	// rest alone in the staff
				// fall thru
			case C.NOTE:
			case C.GRACE:
				break
			}

			st = s.st;
︙︙
3283
3284
3285
3286
3287
3288
3289
3290

3291
3292
3293
3294







3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
			}

			v_s.s = s
			v_s.st = s.st
			v_s.end_time = s.time + s.dur
			if (s.fmr)			// if full meeasure rest
				v_s.end_time -= s.p_v.wmeasure * .3
			if (!s.seqst) {

				s2 = s.ts_prev
					if (s2.st == s.st
					 && s.ymx > s2.ymn
					 && !s2.invis) {







						if (s2.type == C.NOTE) {
							v_s.d = s2.ymn - s.ymx
							break
						}
						if (s2.type == C.REST
						 && s2.y < 18
						 && s.y >= 6)
							v_s.d = -6
					}
			}
			continue
		case C.NOTE:
			if (s.invis || !s.multi)
				continue
			break
		}

		// check if any clash with a rest







|
>
|
|
<
|
>
>
>
>
>
>
>
|
|
|
|
|
|
|
|
<
<
|







3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387

3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403


3404
3405
3406
3407
3408
3409
3410
3411
			}

			v_s.s = s
			v_s.st = s.st
			v_s.end_time = s.time + s.dur
			if (s.fmr)			// if full meeasure rest
				v_s.end_time -= s.p_v.wmeasure * .3
			if (s.seqst)
				continue
			s2 = s.ts_prev
			if (s2.st != s.st

			 || s2.invis)
				continue
			d = s2.ymn
			if (v_s_tb[s2.v] && v_s_tb[s2.v].d
			 && v_s_tb[s2.v] >= s.time)
				d += v_s_tb[s2.v].d
			if (s.ymx <= d)
				continue
			if (s2.type == C.NOTE) {
				v_s.d = d - s.ymx
				break
			}
			if (s2.type == C.REST
			 && s2.y < 18
			 && s.y >= 6)
				v_s.d = (d - s.ymx) / 2


			break
		case C.NOTE:
			if (s.invis || !s.multi)
				continue
			break
		}

		// check if any clash with a rest
︙︙
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861

	// set the clefs and adjust the pitches of all symbol
	set_clefs();
	self.set_pitch(null)
}

// get the left offsets of the first and other staff systems
// return [lsh1, lsho]
function get_lshift() {
    var	st, v, p_v, p1, po, fnt, w,
	sy = cur_sy,
	lsh1 = 0,
	lsho = 0, 
	nv = voice_tb.length

	// get the max width of a voice name/subname
	function get_wx(p, wx) {
	    var	w, j,
		i = 0








|



|
<







3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952

3953
3954
3955
3956
3957
3958
3959

	// set the clefs and adjust the pitches of all symbol
	set_clefs();
	self.set_pitch(null)
}

// get the left offsets of the first and other staff systems
// return left shift [no name, first music line, other lines
function get_lshift() {
    var	st, v, p_v, p1, po, fnt, w,
	sy = cur_sy,
	lsh = [0, 0, 0],

	nv = voice_tb.length

	// get the max width of a voice name/subname
	function get_wx(p, wx) {
	    var	w, j,
		i = 0

︙︙
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911

3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930

3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
		p1 = p_v.nm
		po = p_v.snm
		if ((p1 || po) && !fnt) {
			set_font("voice")
			fnt = gene.deffont
		}
		if (p1) {
			w = get_wx(p1, lsh1)
			if (w > lsh1)
				lsh1 = w
		}
		if (po) {
			w = get_wx(po, lsho)
			if (w > lsho)
				lsho = w
		}
	}
	// add the width of the braces/brackets
	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]
} // get_lshift()

/* -- return the left indentation of the staves -- */
function set_indent(lsh) {
    var	st, v, w, p_voice, p, i, j, font,
	vnt = 0,
	fmt = tsnext ? tsnext.fmt : cfmt

	// name or subname?
	if (fmt.systnames) {		// display the names in the staff system
	    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		// full name
			break
		}
		if (p_voice.snm)
			vnt = 1		// subname
	    }
	}
	gene.vnt = vnt			// voice name type for draw
	return vnt == 2 ? lsh[0] : lsh[1]
}

/* -- decide on beams and on stem directions -- */
/* this routine is called only once per tune */
function set_beams(sym) {
    var	s, t, g, beam, s_opp, n, m, mid_p, pu, pd,
	laststem = -1







|
|
|


|
|
|


















>
|
|
|









|






>
|
|



|



|







3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
		p1 = p_v.nm
		po = p_v.snm
		if ((p1 || po) && !fnt) {
			set_font("voice")
			fnt = gene.deffont
		}
		if (p1) {
			w = get_wx(p1, lsh[1])
			if (w > lsh[1])
				lsh[1] = w
		}
		if (po) {
			w = get_wx(po, lsh[2])
			if (w > lsh[2])
				lsh[2] = w
		}
	}
	// add the width of the braces/brackets
	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
	}
	lsh[0] = w
	lsh[1] += w
	lsh[2] += w
	return lsh
} // get_lshift()

/* -- return the left indentation of the staves -- */
function set_indent(lsh) {
    var	st, v, w, p_voice, p, i, j, font,
	vnt = 0,
	fmt = tsnext ? tsnext.fmt : cfmt

	// name or subname?
	if (fmt.systvoices) {		// put the voice names in the staff system
	    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
		 && (fmt.systvoices == 1
		  || (p_voice.new_name && fmt.systvoices == 3))) {
			vnt = 1		// full name
			break
		}
		if (p_voice.snm)
			vnt = 2		// subname
	    }
	}
	gene.vnt = vnt			// voice name type for draw
	return lsh[vnt]
}

/* -- decide on beams and on stem directions -- */
/* this routine is called only once per tune */
function set_beams(sym) {
    var	s, t, g, beam, s_opp, n, m, mid_p, pu, pd,
	laststem = -1
︙︙
4152
4153
4154
4155
4156
4157
4158


4159
4160
4161
4162
4163
4164
4165
		for (i1 = i11; i1 < i12; i1++) {
			s1.notes[i1].invis = true
			delete s1.notes[i1].acc
		}
		for (i1 = 0; i1 <= s1.nhd; i1++)
			s1.notes[i1].shhd += sh2
	}


	return true
}

/* handle unison with different accidentals */
function unison_acc(s1, s2, i1, i2) {
    var	m, d, acc








>
>







4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
		for (i1 = i11; i1 < i12; i1++) {
			s1.notes[i1].invis = true
			delete s1.notes[i1].acc
		}
		for (i1 = 0; i1 <= s1.nhd; i1++)
			s1.notes[i1].shhd += sh2
	}
	if (s1.dots == s2.dots)			// let the dots in one voice
		s2.dots = 0
	return true
}

/* handle unison with different accidentals */
function unison_acc(s1, s2, i1, i2) {
    var	m, d, acc

︙︙
4335
4336
4337
4338
4339
4340
4341




4342
4343
4344
4345
4346
4347
4348
	for (s = tsfirst; s; s = s.ts_next) {
		if (s.type != C.NOTE
		 || s.invis) {
			if (s.type == C.STAVES)
				sy = s.sy
			continue
		}





		/* treat the stem on two staves with different directions */
		if (s.xstem
		 && s.ts_prev.stem < 0) {
			for (m = 0; m <= s.nhd; m++) {
				s.notes[m].shhd -= 7;		// stem_xoff
				s.notes[m].shac += 16







>
>
>
>







4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
	for (s = tsfirst; s; s = s.ts_next) {
		if (s.type != C.NOTE
		 || s.invis) {
			if (s.type == C.STAVES)
				sy = s.sy
			continue
		}

		// set the dot vertical offset of secondary voices
		if (s.second)
			s.dot_low = 1 //true

		/* treat the stem on two staves with different directions */
		if (s.xstem
		 && s.ts_prev.stem < 0) {
			for (m = 0; m <= s.nhd; m++) {
				s.notes[m].shhd -= 7;		// stem_xoff
				s.notes[m].shac += 16
︙︙
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
			 && s2.st == s.st)
				break
		}
		if (!s2)
			continue
		s1 = s

		/* set the dot vertical offset */
		if (sy.voices[s1.v].range < sy.voices[s2.v].range)
			s2.dot_low = true
		else
			s1.dot_low = true

		/* no shift if no overlap */
		if (s1.ymn > s2.ymx
		 || s1.ymx < s2.ymn)
			continue

		if (same_head(s1, s2))
			continue







<
<
<
<
<
<







4470
4471
4472
4473
4474
4475
4476






4477
4478
4479
4480
4481
4482
4483
			 && s2.st == s.st)
				break
		}
		if (!s2)
			continue
		s1 = s







		/* no shift if no overlap */
		if (s1.ymn > s2.ymx
		 || s1.ymx < s2.ymn)
			continue

		if (same_head(s1, s2))
			continue
︙︙
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
		if (t < 0) {	/* unison and different accidentals */
			unison_acc(s1, s2, i1, i2)
			continue
		}

		sd = 0;
		if (s1.dots) {
			if (s2.dots) {
				if (!t)			/* if no dot clash */
					sd = 1		/* align the dots */
			} else {
				v_invert()		// shift the first voice
			}
		} else if (s2.dots) {
			if (d2 + dr < d + dr2)
				sd = 1		/* align the dots */
		}
		pl = left2;
		pr = right2
		if (!s3 && d2 + dr < d + dr2)







|
|
|
<
<
<







4607
4608
4609
4610
4611
4612
4613
4614
4615
4616



4617
4618
4619
4620
4621
4622
4623
		if (t < 0) {	/* unison and different accidentals */
			unison_acc(s1, s2, i1, i2)
			continue
		}

		sd = 0;
		if (s1.dots) {
			if (!s2.dots
			 || !t)			// if no dot clash
				sd = 1		// align the dots



		} else if (s2.dots) {
			if (d2 + dr < d + dr2)
				sd = 1		/* align the dots */
		}
		pl = left2;
		pr = right2
		if (!s3 && d2 + dr < d + dr2)
︙︙
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
					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 (ymn < ymin)
					ymin = ymn
				else if (ymx > ymax)
					ymax = ymx;
				g.ymx = ymx;
				g.ymn = ymn
			}







|
|







4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
					g.ys = ymx + slen;
					ymx = Math.round(g.ys)
				} else {
					g.y = ymx;
					g.ys = ymn - slen;
					ymn = Math.round(g.ys)
				}
				ymx += 2
				ymn -= 2
				if (ymn < ymin)
					ymin = ymn
				else if (ymx > ymax)
					ymax = ymx;
				g.ymx = ymx;
				g.ymn = ymn
			}
︙︙
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702













4703
4704
4705
4706
4707
4708
4709
			    if (s2.nflags > nflags)
				nflags = s2.nflags
		}

		/* set height of stem end */
		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		/* tremolo */













		if (s.stemless) {
			if (s.stem >= 0) {
				s.y = ymn;
				s.ys = ymx
			} else {
				s.ys = ymn;
				s.y = ymx







|















>
>
>
>
>
>
>
>
>
>
>
>
>







4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
			    if (s2.nflags > nflags)
				nflags = s2.nflags
		}

		/* set height of stem end */
		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		/* tremolo */
		if (s.decstm) {				// if deco on the stem
			if (nflags <= 0) {
				if (slen < s.decstm + 6)
					slen = s.decstm + 6
			} else {
			    var	t = nflags * 4		// beams

				if (s.beam_st & s.beam_end)
					t += 2		// flags
				if (slen < s.decstm + 4 + t)
					slen = s.decstm + 4 + t
			}
		}
		if (s.stemless) {
			if (s.stem >= 0) {
				s.y = ymn;
				s.ys = ymx
			} else {
				s.ys = ymn;
				s.y = ymx
︙︙
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
				}
			}
		}
	} // set_brace()

	// set the top and bottom of the staves
	function set_top_bot() {
	    var	st, p_staff, i, j, l

		for (st = 0; st <= nstaff; st++) {
			p_staff = staff_tb[st]

			// ledger lines
			// index = line number
			// values = [x symbol, x start, x stop]







|







4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
				}
			}
		}
	} // set_brace()

	// set the top and bottom of the staves
	function set_top_bot() {
	    var	st, p_staff, i, l

		for (st = 0; st <= nstaff; st++) {
			p_staff = staff_tb[st]

			// ledger lines
			// index = line number
			// values = [x symbol, x start, x stop]
︙︙
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
				switch (p_staff.stafflines[i]) {
				case '.':
				case '-':
					continue
				}
				break
			}
			p_staff.botline = p_staff.botbar = i * 6
			if (i >= l - 2) {		// 0, 1 or 2 lines
				if (p_staff.stafflines[i] != '.') {
					p_staff.botbar -= 6;
					p_staff.topbar += 6
				} else {		// no line: big bar
					p_staff.botbar -= 12;
					p_staff.topbar += 12
					continue	// no helper line
				}
			}
			if (!non_empty_gl[st])
				continue

			// define the helper lines
			p_staff.hll = 17 + i * 2	// pitch of lowest note
							// without helper line
							// ('D' when standard staff)
			p_staff.hlmap = new Int8Array((l - i + 1) * 2
						+ 2)	// (bug android 4.0)
			for (j = 1; i < l; i++, j += 2) {
				switch (p_staff.stafflines[i]) {
				case '|':
				case '[':
					p_staff.hlmap[j - 1] = 1; // no helper line
					p_staff.hlmap[j] = 1;
					p_staff.hlmap[j + 1] = 1
					break
				}
			}
		}
	} // set_top_bot()

	// remove the staff system at start of line
	if (tsfirst.type == C.STAVES) {
		s = tsfirst
		tsfirst = tsfirst.ts_next







|












<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034

















5035
5036
5037
5038
5039
5040
5041
				switch (p_staff.stafflines[i]) {
				case '.':
				case '-':
					continue
				}
				break
			}
			p_staff.botbar = i * 6
			if (i >= l - 2) {		// 0, 1 or 2 lines
				if (p_staff.stafflines[i] != '.') {
					p_staff.botbar -= 6;
					p_staff.topbar += 6
				} else {		// no line: big bar
					p_staff.botbar -= 12;
					p_staff.topbar += 12
					continue	// no helper line
				}
			}
			if (!non_empty_gl[st])
				continue

















		}
	} // set_top_bot()

	// remove the staff system at start of line
	if (tsfirst.type == C.STAVES) {
		s = tsfirst
		tsfirst = tsfirst.ts_next
︙︙
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
		if (s.seqst) {
			xmin += s.shrink
			if (xmin > width) {
				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 < s.shrink) {
					xse += s.shrink;
					xx += s.shrink
				} else {
//					xx += s.space * spf + s.shrink * (1 - spf)







|







5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
		if (s.seqst) {
			xmin += s.shrink
			if (xmin > width) {
				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 < s.shrink) {
					xse += s.shrink;
					xx += s.shrink
				} else {
//					xx += s.space * spf + s.shrink * (1 - spf)
︙︙
5150
5151
5152
5153
5154
5155
5156


5157
5158
5159
5160
5161
5162
5163
			if (xx == xse)			// if no space
				xx += 10
			spf = (width - xs - xse) / (xx - xse)
		} else {
			spf = spf_last
			if (ll && spf < s.fmt.stretchlast)
				spf = s.fmt.stretchlast


			if (spf > (width - xs) / xx)
				spf = (width - xs) / xx
		}
		while (--cnt >= 0) {
			xx = 0;
			xse = 0;
			x = 0







>
>







5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
			if (xx == xse)			// if no space
				xx += 10
			spf = (width - xs - xse) / (xx - xse)
		} else {
			spf = spf_last
			if (ll && spf < s.fmt.stretchlast)
				spf = s.fmt.stretchlast
			else if (spf < 1 - cfmt.maxshrink)
				spf = 1 - cfmt.maxshrink
			if (spf > (width - xs) / xx)
				spf = (width - xs) / xx
		}
		while (--cnt >= 0) {
			xx = 0;
			xse = 0;
			x = 0
︙︙
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
	}
	tsfirst = null			/* no more notes */
}

/* -- generate the music -- */
// (possible hook)
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)			// if many voices
		self.set_stem_dir()	// set the stems direction in 'multi'

	for (v = 0; v < nv; v++)







|







5365
5366
5367
5368
5369
5370
5371
5372
5373
5374
5375
5376
5377
5378
5379
	}
	tsfirst = null			/* no more notes */
}

/* -- generate the music -- */
// (possible hook)
Abc.prototype.output_music = function() {
    var v, lwidth, indent, lsh, line_height, ts1st, tslast, p_v, meter1,
	nv = voice_tb.length

	set_global()
	if (nv > 1)			// if many voices
		self.set_stem_dir()	// set the stems direction in 'multi'

	for (v = 0; v < nv; v++)
︙︙
5318
5319
5320
5321
5322
5323
5324

5325
5326
5327
5328
5329
5330
5331
	}

	// save symbol pointers for play
	ts1st = tsfirst
	v = nv
	while (--v >= 0)
		voice_tb[v].osym = voice_tb[v].sym


	spf_last = .65				// last spacing factor
	while (1) {				/* loop per music line */
		set_piece();
		indent = set_indent(lsh)
		if (!line_height
		 && cfmt.indent







>







5413
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
	}

	// save symbol pointers for play
	ts1st = tsfirst
	v = nv
	while (--v >= 0)
		voice_tb[v].osym = voice_tb[v].sym
	meter1 = ts1st.p_v.meter

	spf_last = .65				// last spacing factor
	while (1) {				/* loop per music line */
		set_piece();
		indent = set_indent(lsh)
		if (!line_height
		 && cfmt.indent
︙︙
5376
5377
5378
5379
5380
5381
5382

5383
	v = nv
	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.sym = p_v.osym
	}

}







>

5472
5473
5474
5475
5476
5477
5478
5479
5480
	v = nv
	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.sym = p_v.osym
	}
	ts1st.p_v.meter = meter1
}
Changes to core/parse.js.
︙︙
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
	char_tb[k] = v
}

// get a stafflines value
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 //undefined
	return "||||||||||||||||".slice(0, n)
}

// create a block symbol in the tune body
function new_block(subtype) {
    var	c_v,
	s = {
			type: C.BLOCK,
			subtype: subtype,
			dur: 0
		}

	c_v = curvoice
	if (subtype.slice(0, 4) != "midi")	// if not a play command
		curvoice = voice_tb[0]		// set the block in the first voice
	sym_link(s)
	if (c_v)
		curvoice = c_v
	return s
}

// set the voice parameters
// (possible hook)
Abc.prototype.set_vp = function(a) {
    var	s, item, pos, val, clefpit,







|
















<
|





<
<
<

<
<







306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329

330
331
332
333
334
335



336


337
338
339
340
341
342
343
	char_tb[k] = v
}

// get a stafflines value
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 //undefined
	return "||||||||||||||||".slice(0, n)
}

// create a block symbol in the tune body
function new_block(subtype) {

    var	s = {
			type: C.BLOCK,
			subtype: subtype,
			dur: 0
		}




	sym_link(s)


	return s
}

// set the voice parameters
// (possible hook)
Abc.prototype.set_vp = function(a) {
    var	s, item, pos, val, clefpit,
︙︙
506
507
508
509
510
511
512
513
514


515
516
517
518
519
520
521
522
523
524
525
		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







|

>
>
|

<
|







500
501
502
503
504
505
506
507
508
509
510
511
512

513
514
515
516
517
518
519
520
		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")
				break
			}
			if (curvoice.st != undefined)
				par_sy.staves[curvoice.st].stafflines = val

			curvoice.stafflines = val
			break
		case "staffnonote=":
			val = +a.shift()
			if (isNaN(val))
				syntax(1, "Bad %%staffnonote value")
			else
				curvoice.staffnonote = val
︙︙
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
	s.k_sf = sf;

	// set the map of the notes with accidentals
	s.k_map = s.k_bagpipe && !sf
		? abc2svg.keys[9]		// implicit F# and C#
		: 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








|
|







767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
	s.k_sf = sf;

	// set the map of the notes with accidentals
	s.k_map = s.k_bagpipe && !sf
		? abc2svg.keys[9]		// implicit F# and C#
		: abc2svg.keys[sf + 7]
	if (s.k_a_acc) {
		s.k_map = Array.prototype.slice.call(s.k_map)	// simple Array
		i = s.k_a_acc.length			// (for micro-accidentals)
		while (--i >= 0) {
			note = s.k_a_acc[i]
			s.k_map[(note.pit + 19) % 7] = note.acc
		}
	}
	s.k_mode = mode

︙︙
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
					if (p[i] != ' ' && p[i] != '+')
						break
					if (i >= p.length
					 || p[i + 1] == '(')	/* "M:5 (2/4+3/4)" */
						break
					meter.top += p[i++]
				}
				m1 = eval(meter.top)
				break
			}
			if (!in_parenth) {
				if (meter.bot)
					m2 = +meter.bot
				wmeasure += m1 * C.BLEN / m2
			}







|







899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
					if (p[i] != ' ' && p[i] != '+')
						break
					if (i >= p.length
					 || p[i + 1] == '(')	/* "M:5 (2/4+3/4)" */
						break
					meter.top += p[i++]
				}
				m1 = eval(meter.top.replace(/ /g, '+'))
				break
			}
			if (!in_parenth) {
				if (meter.bot)
					m2 = +meter.bot
				wmeasure += m1 * C.BLEN / m2
			}
︙︙
1749
1750
1751
1752
1753
1754
1755

1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767








1768
1769


















1770
1771
1772

1773
1774
1775



1776



1777




1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
// return the mapping of a note
//
// The global 'maps' object is indexed by the map name.
// Its content is an object ('map') indexed from the map type:
// - normal = ABC note
// - octave = 'o' + ABC note in C..B interval
// - key    = 'k' + scale index

// - all    = 'all'
// The 'map' is stored in the note. It is an array of
//	[0] array of heads (glyph names)
//	[1] print (note)
//	[2] color
//	[3] play (note)
function set_map(p_v, note, acc,
		 trp) {			// transpose done
    var	nn = not2abc(note.pit, acc),
	map = maps[p_v.map]		// never null

	if (!map[nn]) {








		nn = 'o' + nn.replace(/[',]+/, '')	// ' octave
		if (!map[nn]) {


















			nn = 'k' + ntb[(note.pit + 75 -
					p_v.ckey.k_sf * 11) % 7]
			if (!map[nn]) {

				nn = 'all'		// 'all'
				if (!map[nn])
					return



			}



		}




	}
	map = map[nn]
	if (!trp) {				// transpose not done yet
		if (map[1]) {			// if note shift
			note.pit = map[1].pit
			note.acc = map[1].acc
			if (map[1].notrp) {
				note.notrp = 1 //true	// no transpose
				note.noplay = 1 //true	// no play
			}
		}
		return
	}
	note.map = map

	if (map[2])				// if color
		note.color = map[2]
	nn = map[3]
	if (nn)					// if play map







>
|






|

|

|
>
>
>
>
>
>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
>
|
|
|
>
>
>
|
>
>
>
|
>
>
>
>

|
|
|






<
<







1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820


1821
1822
1823
1824
1825
1826
1827
// return the mapping of a note
//
// The global 'maps' object is indexed by the map name.
// Its content is an object ('map') indexed from the map type:
// - normal = ABC note
// - octave = 'o' + ABC note in C..B interval
// - key    = 'k' + scale index
// - tonic  = 't' + mode index
// - any    = '*'
// The 'map' is stored in the note. It is an array of
//	[0] array of heads (glyph names)
//	[1] print (note)
//	[2] color
//	[3] play (note)
function set_map(p_v, note, acc,
		trp_p) {			// flag "do transpose?"
    var	nn = not2abc(note.pit, acc),
	map = maps[p_v.map]

	if (!map)
		return

	// test if 'nn' is in the map
	function map_p() {
		if (map[nn])
			return 1 //true
	    var	sf, d

		nn = 'o' + nn.replace(/[',]+/, '')	// '
		if (map[nn])
			return 1 //true
//fixme: useless
		nn = 'k' + ['__','_','=','^','^^','='][acc + 2]	// key chromatic
			+ ntb[(note.pit + 75 - p_v.ckey.k_sf * 11) % 7]
		if (map[nn])
			return 1 //true
		nn = nn.replace(/[_=^]/g,'')		// key diatonic
		if (map[nn])
			return 1 //true
		sf = p_v.ckey.k_sf + [0, 2, 4, -1, 1, 3, -2][p_v.ckey.k_mode]
		if (sf < -7)
			sf += 7
		else if (sf > 7)
			sf -= 7
		d = abc2svg.keys[sf + 7]
				[(note.pit + 75) % 7]
		nn = 't' + ['__','_','=','^','^^','=']	// tonic chromatic
				[acc - d + 2]
			+ ntb[(note.pit + 75 - p_v.ckey.k_mode
				 - p_v.ckey.k_sf * 11) % 7]
		if (map[nn])
			return 1 //true
		nn = nn.replace(/[_=^]/g,'')		// tonic diatonic
		if (map[nn])
			return 1 //true
		nn = '*'				// any note
		return map[nn]
	} // map_p()

	if (!map_p())					// note in the map?
		return					// no
	map = map[nn]

	if (trp_p) {
		if (map[1] && map[1].notrp)
			note.notrp = 1 //true	// no transpose
		return
	}

	if (map[1]				// if note transpose
	 && !note.map) {			// for the first time
			note.pit = map[1].pit
			note.acc = map[1].acc
			if (map[1].notrp) {
				note.notrp = 1 //true	// no transpose
				note.noplay = 1 //true	// no play
			}


	}
	note.map = map

	if (map[2])				// if color
		note.color = map[2]
	nn = map[3]
	if (nn)					// if play map
︙︙
1999
2000
2001
2002
2003
2004
2005
2006
2007

2008
2009
2010
2011
2012
2013
2014
		error(1, tie_s, "Bad tie")
	else
		s.ti2 = true
} // do_ties()

// (possible hook)
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		// save parsed decoration names

	a_dcn = []
	parse.stemless = false;







|
|
>







2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
		error(1, tie_s, "Bad tie")
	else
		s.ti2 = true
} // do_ties()

// (possible hook)
Abc.prototype.new_note = function(grace, sls) {
    var	note, s, in_chord, c, tie_s, acc_tie,
	i, n, s2, nd, res, num, apit, div, ty,
	chdur = 1,
	dpit = 0,
	sl1 = [],
	line = parse.line,
	a_dcn_sav = a_dcn		// save parsed decoration names

	a_dcn = []
	parse.stemless = false;
︙︙
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
		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

		// ignore if in second voice
		if (curvoice.second) {
			delete curvoice.eoln	// ignore the end of line
			curvoice.time += s.dur
			return //null
		}

		// convert 'Z'/'Z1' to a whole measure rest
		if (s.nmes == 1) {
			s.type = C.REST;
			s.dur_orig = s.dur;
			s.fmr = 1		// full measure rest
			s.notes = [{
				pit: 18,







<
<
<
<
<
<
<







2082
2083
2084
2085
2086
2087
2088







2089
2090
2091
2092
2093
2094
2095
		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








		// convert 'Z'/'Z1' to a whole measure rest
		if (s.nmes == 1) {
			s.type = C.REST;
			s.dur_orig = s.dur;
			s.fmr = 1		// full measure rest
			s.notes = [{
				pit: 18,
︙︙
2108
2109
2110
2111
2112
2113
2114











2115
2116
2117
2118
2119
2120
2121
			pit: 18,
			dur: s.dur_orig
		}]
		break
	case '[':			// chord
		in_chord = true;
		c = line.next_char()











		// fall thru
	default:			// accidental, chord, note
		if (curvoice.acc_tie) {
			acc_tie = curvoice.acc_tie
			curvoice.acc_tie = null
		}
		s.notes = []







>
>
>
>
>
>
>
>
>
>
>







2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
			pit: 18,
			dur: s.dur_orig
		}]
		break
	case '[':			// chord
		in_chord = true;
		c = line.next_char()
		i = line.buffer.indexOf(']', line.index)
		if (i < 0) {
			syntax(1, "No end of chord")
			return
		}
		n = line.index			// save the parser index
		line.index = i + 1		// set the parser to the end of chord
		nd = parse_dur(line)
		chdur = nd[0] / nd[1]		// length factor of the chord
		in_chord = reg_dur.lastIndex	// hack: index after the chord length
		line.index = n			// restore the parser index
		// fall thru
	default:			// accidental, chord, note
		if (curvoice.acc_tie) {
			acc_tie = curvoice.acc_tie
			curvoice.acc_tie = null
		}
		s.notes = []
︙︙
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160

2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176

2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207


2208
2209
2210
2211
2212
2213
2214
					if (!c)
						break
					i = c.charCodeAt(0);
					if (i >= 128) {
						syntax(1, errs.not_ascii)
						return //null
					}
					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()	// line -> a_dcn
						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 //null


			if (curvoice.octave)
				note.pit += curvoice.octave * 7

			// get the real accidental
			apit = note.pit + 19		// pitch from C-1
			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]	// tied note

			// set the MIDI pitch
			if (!note.midi)		// if not map play
				note.midi = pit2mid(apit, i)

			// transpose
			if (curvoice.map
			 && maps[curvoice.map])
				set_map(curvoice, note, i)	// transpose not done
			if (curvoice.tr_sco) {
			    if (!note.notrp) {
				i = nt_trans(note, i)
				if (i == -3) {		// if triple sharp/flat
					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



//fixme: does not work if transposition
			if (i) {
				switch (cfmt["writeout-accidentals"][1]) {
				case 'd':			// added
					s2 = curvoice.ckey
					if (!s2.k_a_acc)







|
|





|
|
















>















|
>














|
<
|
<
|












>
>







2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229

2230

2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
					if (!c)
						break
					i = c.charCodeAt(0);
					if (i >= 128) {
						syntax(1, errs.not_ascii)
						return //null
					}
					ty = char_tb[i]
					switch (ty[0]) {
					case '(':
						sl1.push(parse_vpos());
						c = line.char()
						continue
					case '!':
						if (ty.length > 1)
							a_dcn.push(ty.slice(1, -1))
						else
							get_deco()	// line -> a_dcn
						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 //null

			note.dur *= chdur		// chord factor
			if (curvoice.octave)
				note.pit += curvoice.octave * 7

			// get the real accidental
			apit = note.pit + 19		// pitch from C-1
			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
			 && !curvoice.ckey.k_drum) {
				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]	// tied note

			// set the MIDI pitch
			if (!note.midi)		// if not map play
				note.midi = pit2mid(apit, i)

			// transpose
			if (curvoice.tr_sco) {

				set_map(curvoice, note, i, 1)	// possible transpose?

			    if (!note.notrp) {			// yes
				i = nt_trans(note, i)
				if (i == -3) {		// if triple sharp/flat
					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 (curvoice.map)
				set_map(curvoice, note, i)

//fixme: does not work if transposition
			if (i) {
				switch (cfmt["writeout-accidentals"][1]) {
				case 'd':			// added
					s2 = curvoice.ckey
					if (!s2.k_a_acc)
︙︙
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
					curvoice.sls.push({
						ty: i,
						ss: s,
						nts: note	// starting note
					})
				}
			}
			if (a_dcn.length) {
				s.time = curvoice.time	// (needed for !tie)!
				dh_cnv(s, note)
			}
			s.notes.push(note)
			if (!in_chord)
				break

			// in chord: get the ending slurs and the ties
			c = line.char()
			while (1) {







<
<
<
<







2274
2275
2276
2277
2278
2279
2280




2281
2282
2283
2284
2285
2286
2287
					curvoice.sls.push({
						ty: i,
						ss: s,
						nts: note	// starting note
					})
				}
			}




			s.notes.push(note)
			if (!in_chord)
				break

			// in chord: get the ending slurs and the ties
			c = line.char()
			while (1) {
︙︙
2282
2283
2284
2285
2286
2287
2288





2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
						continue
					}
					syntax(1, "Misplaced dot")
					break
				}
				break
			}





			if (c == ']') {
				line.index++;

				// adjust the chord duration
				nd = parse_dur(line);
				s.nhd = s.notes.length - 1
				for (i = 0; i <= s.nhd ; i++) {
					note = s.notes[i];
					note.dur = note.dur * nd[0] / nd[1]
				}
				break
			}
		}

		// handle the starting slurs
		if (sls.length) {
			while (1) {







>
>
>
>
>

|
<
<
<

<
<
<
<







2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329



2330




2331
2332
2333
2334
2335
2336
2337
						continue
					}
					syntax(1, "Misplaced dot")
					break
				}
				break
			}
			if (a_dcn.length) {
				s.time = curvoice.time	// (needed for !tie)!
				dh_cnv(s, note)
			}

			if (c == ']') {
				line.index = in_chord



				s.nhd = s.notes.length - 1




				break
			}
		}

		// handle the starting slurs
		if (sls.length) {
			while (1) {
︙︙
2324
2325
2326
2327
2328
2329
2330











2331
2332
2333
2334
2335
2336
2337
	}
	if (s.grace && s.type != C.NOTE) {
		syntax(1, errs.bad_grace)
		return //null
	}

	if (s.notes) {				// if note or rest











		if (!grace) {
			switch (curvoice.pos.stm & 0x07) {
			case C.SL_ABOVE: s.stem = 1; break
			case C.SL_BELOW: s.stem = -1; break
			case C.SL_HIDDEN: s.stemless = true; break
			}








>
>
>
>
>
>
>
>
>
>
>







2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
	}
	if (s.grace && s.type != C.NOTE) {
		syntax(1, errs.bad_grace)
		return //null
	}

	if (s.notes) {				// if note or rest
		if (!s.fmr) {			// (but not full measure rest)
			n = s.dur_orig / 12	// check its duration
			i = 0
			while (!(n & 1)) {
				n >>= 1
				i++
			}
			if ((n + 1) & n)
				error(0, s, "Non standard note duration $1",
					n + '/' + (1 << (6 - i)))
		}
		if (!grace) {
			switch (curvoice.pos.stm & 0x07) {
			case C.SL_ABOVE: s.stem = 1; break
			case C.SL_BELOW: s.stem = -1; break
			case C.SL_HIDDEN: s.stemless = true; break
			}

︙︙
Changes to core/subs.js.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
// abc2svg - subs.js - text output
//
// Copyright (C) 2014-2023 Jean-Francois Moine
//
// This file is part of abc2svg-core.
//
// abc2svg-core is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// abc2svg-core 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg-core.  If not, see <http://www.gnu.org/licenses/>.

// add font styles
    var	sheet
var add_fstyle = typeof document != "undefined" ?
    function(s) {
    var	e
	if (cfmt.fullsvg)
		font_style += "\n" + s
	if (!sheet) {
		if (abc2svg.sheet) {	// if styles from a previous generation
			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)	// insert each style
	while (1) {
		e = s.shift()
		if (!e)
			break
		sheet.insertRule(e, sheet.cssRules.length)
	}


|


















|
|

|
|
<
<
|
<
<
<
<
|
|
|
|
<
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26


27




28
29
30
31

32
33
34
35
36
37
38
39
// abc2svg - subs.js - text output
//
// Copyright (C) 2014-2024 Jean-Francois Moine
//
// This file is part of abc2svg-core.
//
// abc2svg-core is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// abc2svg-core 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg-core.  If not, see <http://www.gnu.org/licenses/>.

// add font styles
    var	sheet
var add_fstyle = abc2svg.el
    ? function(s) {
    var	e

	font_style += "\n" + s


	if (!abc2svg.styles) {




		e = document.createElement('style')
		document.head.appendChild(e)
		abc2svg.styles = e
	}

	sheet = abc2svg.styles.sheet
	s = s.match(/[^{]+{[^}]+}/g)	// insert each style
	while (1) {
		e = s.shift()
		if (!e)
			break
		sheet.insertRule(e, sheet.cssRules.length)
	}
︙︙
116
117
118
119
120
121
122












123
124
125
126
127
128

129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148

149
150
151
152
153
154
155


156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185

186
187
188
189
190
191
192

193
194
195
196
197
198
199
200
201
202
	}
	return (font || gene.curfont).cw_tb[i]
}
// return the character width with the current font
function cwidf(c) {
	return cwid(c) * gene.curfont.swfac
}













// estimate the width and height of a string ..
var strwh

(function() {
    if (typeof document != "undefined") {


    // .. by the browser

	// create a text element if not done yet
      var	el

	// change the function
	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") {	// if string already converted
			el.innerHTML = str
			str.wh = [ el.clientWidth, el.clientHeight ]
			return str.wh
		}

		str = str.replace(/<|>|&[^&\s]*?;|&/g, function(c){
			switch (c) {
			case '<': return "&lt;"
			case '>': return "&gt;"
			case '&': return "&amp;"
			}
			return c		// &xxx;
		})

		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
//fixme: bad width if space(s) at end of string
			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 {








>
>
>
>
>
>
>
>
>
>
>
>





|
>



<
<
<




<
<
<
<
<
<
<
|
<

>







>
>

<






|
<
<
<
<
<
<
<
<













>







>


<







110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138



139
140
141
142







143

144
145
146
147
148
149
150
151
152
153
154
155

156
157
158
159
160
161
162








163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186

187
188
189
190
191
192
193
	}
	return (font || gene.curfont).cw_tb[i]
}
// return the character width with the current font
function cwidf(c) {
	return cwid(c) * gene.curfont.swfac
}

// make XML clean
function clean_txt(p) {
	return p.replace(/<|>|&[^&\s]*?;|&/g, function(c) {
		switch (c) {
		case '<': return "&lt;"
		case '>': return "&gt;"
		case '&': return "&amp;"
		}
		return c		// &xxx;
	})
} // clean_txt()

// estimate the width and height of a string ..
var strwh

(function() {
    if (typeof document != "undefined"
     && abc2svg.el) {

    // .. by the browser




	// change the function
	strwh = function(str) {
		if (str.wh)
			return str.wh









	    var	c,
		el = abc2svg.el,	// hidden <span> created by edit/abcweb/...
		font = gene.curfont,
		h = font.size,
		w = 0,
		n = str.length,
		i0 = 0,
		i = 0

		if (!el.parentElement)		// insert back the <span> in the document
			document.body.appendChild(el)
		el.className = font_class(font)


		if (typeof str == "object") {	// if string already converted
			el.innerHTML = str
			str.wh = [ el.clientWidth, el.clientHeight ]
			return str.wh
		}
		str = clean_txt(str)









		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.className = font_class(font)
			}

			el.innerHTML = str.slice(i0, i >= 0 ? i : undefined)
			w += el.clientWidth
//fixme: bad width if space(s) at end of string
			if (el.clientHeight > h)
				h = el.clientHeight

			if (i < 0)
				break

			i += 2;
			i0 = i
		}
		return [w, h]
	}
    } else {

︙︙
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
			return c		// &xxx;
		})
	if (c_font != o_font)
		o += "</tspan>"

	// convert to String and memorize the string width and height
	o = new String(o)
	if (typeof document != "undefined")
		strwh(o)		// browser
	else
		o.wh = strwh(str)	// CLI

	gene.curfont = c_font	// keep the current font for the next paragraph

	return o







|







292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
			return c		// &xxx;
		})
	if (c_font != o_font)
		o += "</tspan>"

	// convert to String and memorize the string width and height
	o = new String(o)
	if (abc2svg.el)
		strwh(o)		// browser
	else
		o.wh = strwh(str)	// CLI

	gene.curfont = c_font	// keep the current font for the next paragraph

	return o
︙︙
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
function xy_str(x, y,
		str,		// string or object String with attribute 'wh'
		action,		// default: align left
		w,		// needed for justify
		wh) {		// optional [width, height]
	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







|







327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
function xy_str(x, y,
		str,		// string or object String with attribute 'wh'
		action,		// default: align left
		w,		// needed for justify
		wh) {		// optional [width, height]
	if (!wh)
		wh = str.wh || strwh(str)
	if (cfmt.singleline || cfmt.trimsvg) {
	    var wx = wh[0]
		switch (action) {
		case 'c':
			wx = wh[0] / 2
			break
		case 'j':
			wx = w
︙︙
524
525
526
527
528
529
530
531

532
533
534
535
536
537
538
			for (j = 0; j < words.length; j++) {
				ww = strwh(words[j] + ' ')	// &nbsp;
				w += ww[0]
				if (w >= 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]
			}







|
>







515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
			for (j = 0; j < words.length; j++) {
				ww = strwh(words[j] + ' ')	// &nbsp;
				w += ww[0]
				if (w >= strlw) {
					vskip(wh * cfmt.lineskipfac)
					xy_str(0, ww[1] * .2,
						words.slice(k, j).join(' '),
						action, strlw,
						[w - ww[0], ww[1]])
					k = j;
					w = ww[0]
					wh = 0
				}
				if (ww[1] > wh)
					wh = ww[1]
			}
︙︙
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615






616
617
618
619
620
621
622
	    var	y = gene.curfont.size * .22		// descent
		if (k != 0)
			xy_str(x, y, p.slice(0, k), 'r')
		if (i < p.length)
			xy_str(x + 5, y, p.slice(i), 'l')
	} // put_wline()

	set_font("words")
	vskip(cfmt.wordsspace)
	svg_flush()

	// estimate the width of the lines
	words = words.split('\n')
	nw = words.length
	for (i = 0; i < nw; i++) {
		p = words[i]
		if (!p) {
			while (i + 1 < nw && !words[i + 1])
				i++
			n++
		} else if (p.length > maxn) {
			maxn = p.length
			i1 = i		// keep this line
		}
	}







	w = get_lwidth() / 2		// half line width
	lw = strwh(words[i1])[0]
	i1 = i2 = 0
	if (lw < w) {			// if 2 columns
		j = n >> 1
		for (i = 0; i < nw; i++) {







<
<
<
<














>
>
>
>
>
>







583
584
585
586
587
588
589




590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
	    var	y = gene.curfont.size * .22		// descent
		if (k != 0)
			xy_str(x, y, p.slice(0, k), 'r')
		if (i < p.length)
			xy_str(x + 5, y, p.slice(i), 'l')
	} // put_wline()





	// estimate the width of the lines
	words = words.split('\n')
	nw = words.length
	for (i = 0; i < nw; i++) {
		p = words[i]
		if (!p) {
			while (i + 1 < nw && !words[i + 1])
				i++
			n++
		} else if (p.length > maxn) {
			maxn = p.length
			i1 = i		// keep this line
		}
	}
	if (i1 == undefined)
		return			// no text in the W: lines!

	set_font("words")
	vskip(cfmt.wordsspace)
	svg_flush()

	w = get_lwidth() / 2		// half line width
	lw = strwh(words[i1])[0]
	i1 = i2 = 0
	if (lw < w) {			// if 2 columns
		j = n >> 1
		for (i = 0; i < nw; i++) {
︙︙
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
	P: "parts",
	Q: "tempo",
	R: "info",
	T: "title",
	X: "title"
}
function write_headform(lwidth) {
    var	c, font, font_name, align, x, y, sz, w,
		info_val = {},
		info_font = Object.create(info_font_init),
		info_sz = {
			A: cfmt.infospace,
			C: cfmt.composerspace,
			O: cfmt.composerspace,
			R: cfmt.infospace







|







725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
	P: "parts",
	Q: "tempo",
	R: "info",
	T: "title",
	X: "title"
}
function write_headform(lwidth) {
    var	c, font, font_name, align, x, y, sz, w, yd,
		info_val = {},
		info_font = Object.create(info_font_init),
		info_sz = {
			A: cfmt.infospace,
			C: cfmt.composerspace,
			O: cfmt.composerspace,
			R: cfmt.infospace
︙︙
878
879
880
881
882
883
884

885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936


















937
938
939
940
941













942
943
944
945
946
947
948
			font = get_font(font_name);
			sz = font.size * 1.1
			if (info_sz[c])
				sz += info_sz[c];
			set_font(font);
			x = xa[align];
			y = ya[align] + sz


			if (c == 'Q') {			/* special case for tempo */
				self.set_width(glovar.tempo)
				if (!glovar.tempo.invis) {
					if (align != 'l') {
						tempo_build(glovar.tempo)
						w = glovar.tempo.tempo_wh[0]

						if (align == 'c')
							w *= .5;
						x -= w
					}
					writempo(glovar.tempo, x, -y)
				}
			} else if (str) {
				if (c == 'T')
					str = trim_title(str,
							 info_font.T[0] == 's')
				xy_str(x, -y, str, align)
			}

			if (c == 'T') {
				font_name = info_font.T = "subtitle";
				info_sz.T = cfmt.subtitlespace
			}
			if (info_nb[c] <= 1) {
				if (c == 'T') {
					font = get_font(font_name);
					sz = font.size * 1.1
					if (info_sz[c])
						sz += info_sz[c];
					set_font(font)
				}
				while (info_val[c].length > 0) {
					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)
}



















/* -- output the tune heading -- */
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







>


















|

















|















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



|

>
>
>
>
>
>
>
>
>
>
>
>
>







872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
			font = get_font(font_name);
			sz = font.size * 1.1
			if (info_sz[c])
				sz += info_sz[c];
			set_font(font);
			x = xa[align];
			y = ya[align] + sz
			yd = y - font.size * .22	// descent

			if (c == 'Q') {			/* special case for tempo */
				self.set_width(glovar.tempo)
				if (!glovar.tempo.invis) {
					if (align != 'l') {
						tempo_build(glovar.tempo)
						w = glovar.tempo.tempo_wh[0]

						if (align == 'c')
							w *= .5;
						x -= w
					}
					writempo(glovar.tempo, x, -y)
				}
			} else if (str) {
				if (c == 'T')
					str = trim_title(str,
							 info_font.T[0] == 's')
				xy_str(x, -yd, str, align)
			}

			if (c == 'T') {
				font_name = info_font.T = "subtitle";
				info_sz.T = cfmt.subtitlespace
			}
			if (info_nb[c] <= 1) {
				if (c == 'T') {
					font = get_font(font_name);
					sz = font.size * 1.1
					if (info_sz[c])
						sz += info_sz[c];
					set_font(font)
				}
				while (info_val[c].length > 0) {
					y += sz;
					str = info_val[c].shift();
					xy_str(x, -yd, 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)
}

// get the meaningful names of a part (P:)
function partname(c) {
    var	i,
	tmp = cfmt.partname.split('\n')

	for (i = 0; i < tmp.length; i++) {
		if (tmp[i][0] == c) {
			c = tmp[i].match(/.\s+(\w+)\s*(.+)?/)
			if (!c[2])
				c[2] = c[1]
			if (c[2][0] == '"')
				c[2] = c[2].slice(1, -1)
			return c
		}
	}
	return [0, c, c]
} // partname()

/* -- output the tune heading -- */
function write_heading() {
    var	i, j, area, composer, origin, rhythm, down1, down2, p,
		lwidth = get_lwidth()

	// build a new sequence of the parts with clearer names
	function new_part() {
	    var	i,
		o = ""

		for (i = 0; i < info.P.length; i++) {
			if (i)
				o += ' '
			o += partname(info.P[i])[1]
		}
		return o
	} // new_part()

	vskip(cfmt.topspace)

	if (cfmt.titleformat) {
		write_headform(lwidth);
		vskip(cfmt.musicspace)
		return
︙︙
1032
1033
1034
1035
1036
1037
1038



1039
1040
1041
1042
1043
1044
1045
	 && 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)
}







>
>
>
|






1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
	 && 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
		p = info.P
		if (cfmt.partname)
			p = new_part()
		xy_str(0, -down2 + gene.curfont.size *.22, p)
		down2 += gene.curfont.pad
	} else if (down1 > down2) {
		down2 = down1
	}
	vskip(down2 + cfmt.musicspace)
}
Changes to core/svg.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - svg.js - svg functions
//
// Copyright (C) 2014-2023 Jean-Francois Moine
//
// This file is part of abc2svg-core.
//
// abc2svg-core is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.


|







1
2
3
4
5
6
7
8
9
10
// abc2svg - svg.js - svg functions
//
// Copyright (C) 2014-2024 Jean-Francois Moine
//
// This file is part of abc2svg-core.
//
// abc2svg-core is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
︙︙
20
21
22
23
24
25
26


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43


44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
var	output = "",		// output buffer
	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,	// default x offset of the images
	posy = 0,		// y offset in the block
	img = {			// image
		width: cfmt.pagewidth,	// width
		lm: cfmt.leftmargin,	// left and right margins
		rm: cfmt.rightmargin,
		wx: 0,			// used width between the left and right margins
		chg: 1 //true
	},
	defined_glyph = {},
	defs = '',
	fulldefs = '',		// unreferenced defs as <filter>
	stv_g = {		/* staff/voice graphic parameters */
		scale: 1,


		dy: 0,
		st: -1,
		v: -1,
		g: 0
//		color: undefined
	},
	blkdiv = 0		// block of contiguous SVGs
				// -1: block started
				//  0: no block
				//  1: start a block
				//  2: start a new page

// glyphs in music font
var tgls = {
 "mtr ": {x:0, y:0, c:"\u0020"},	// space
  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"},	// repeat dots

  dsgn: {x:-12, y:0, c:"\ue045"},	// D.S.
  dcap: {x:-12, y:0, c:"\ue046"},	// D.C.
  sgno: {x:-5, y:0, c:"\ue047"},	// segno
  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"},







>
>

















>
>


















|

|
|
>







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
var	output = "",		// output buffer
	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.sltnW{stroke:currentColor;fill:none;stroke-width:.25}\
\n.sldW{stroke:currentColor;fill:none;stroke-width:.7;stroke-dasharray:5,10}\
\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,	// default x offset of the images
	posy = 0,		// y offset in the block
	img = {			// image
		width: cfmt.pagewidth,	// width
		lm: cfmt.leftmargin,	// left and right margins
		rm: cfmt.rightmargin,
		wx: 0,			// used width between the left and right margins
		chg: 1 //true
	},
	defined_glyph = {},
	defs = '',
	fulldefs = '',		// unreferenced defs as <filter>
	stv_g = {		/* staff/voice graphic parameters */
		scale: 1,
		stsc: 1,	// staff scale
		vsc: 1,		// voice scale
		dy: 0,
		st: -1,
		v: -1,
		g: 0
//		color: undefined
	},
	blkdiv = 0		// block of contiguous SVGs
				// -1: block started
				//  0: no block
				//  1: start a block
				//  2: start a new page

// glyphs in music font
var tgls = {
 "mtr ": {x:0, y:0, c:"\u0020"},	// space
  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:25, c:"\ue039"},
  short: {x:0, y:32, c:"\ue038"},
  tick: {x:0, y:25, c:"\ue039"},
  rdots: {x:0, y:0, c:"\ue043"},	// repeat dots
  rdot: {x:0, y:0, c:"\ue044"},		// single repeat dot
  dsgn: {x:-12, y:0, c:"\ue045"},	// D.S.
  dcap: {x:-12, y:0, c:"\ue046"},	// D.C.
  sgno: {x:-5, y:0, c:"\ue047"},	// segno
  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"},
︙︙
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
  sfz: {x:-10, y:0, c:"\ue539"},
  trl: {x:-5, y:-2, c:"\ue566"},	// trill
  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"},	// plus
  sld: {x:-8, y:12, c:"\ue5d0"},	// slide
  grm: {x:-3, y:-2, c:"\ue5e2"},	// grace mark
  dnb: {x:-3, y:0, c:"\ue610"},		// down bow
  upb: {x:-2, y:0, c:"\ue612"},		// up bow
  opend: {x:-2, y:-2, c:"\ue614"},	// harmonic
  roll: {x:0, y:0, c:"\ue618"},
  thumb: {x:-2, y:-2, c:"\ue624"},
  snap: {x:-2, y:-2, c:"\ue630"},







|







171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
  sfz: {x:-10, y:0, c:"\ue539"},
  trl: {x:-5, y:-2, c:"\ue566"},	// trill
  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"},	// plus
  sld: {x:-8, y:2, c:"\ue5d0"},		// slide
  grm: {x:-3, y:-2, c:"\ue5e2"},	// grace mark
  dnb: {x:-3, y:0, c:"\ue610"},		// down bow
  upb: {x:-2, y:0, c:"\ue612"},		// up bow
  opend: {x:-2, y:-2, c:"\ue614"},	// harmonic
  roll: {x:0, y:0, c:"\ue618"},
  thumb: {x:-2, y:-2, c:"\ue624"},
  snap: {x:-2, y:-2, c:"\ue630"},
︙︙
326
327
328
329
330
331
332
333
334
335
336
337
338
339

340

341

342
343
344
345
346
347
348
349
350

351
352
353
354
355
356
357
358
359
360
361
362
363
364

365
366
367
368
369
370
371
372
373
}

/* -- set the staff scale (only) -- */
function set_sscale(st) {
	var	new_scale, dy

	if (st != stv_g.st && stv_g.scale != 1)
		stv_g.scale = 0;
	new_scale = st >= 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()
}

/* -- set the voice or staff scale -- */
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()
}

// -- set the staff output buffer and scale when delayed output
function set_dscale(st, no_scale) {
	if (output) {







|





|
>

>
|
>








|
>


|
<
<
<
<
<
|
|

|


>

|







331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362





363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
}

/* -- set the staff scale (only) -- */
function set_sscale(st) {
	var	new_scale, dy

	if (st != stv_g.st && stv_g.scale != 1)
		stv_g.scale = 1
	new_scale = st >= 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
	 && stv_g.st == st && stv_g.vsc == 1)
		return
	stv_g.stsc =
		stv_g.scale = new_scale
	stv_g.vsc = 1
	stv_g.dy = dy;
	stv_g.st = st;
	stv_g.v = -1;
	set_g()
}

/* -- set the voice or staff scale -- */
function set_scale(s) {
    var	new_dy = posy,
	st = staff_tb[s.st].staffscale == 1 ? -1 : s.st,
	new_scale = s.p_v.scale

	if (st >= 0) {





		new_scale *= staff_tb[st].staffscale
		new_dy = staff_tb[st].y
	}
	if (new_scale == stv_g.scale && stv_g.dy == new_dy)
		return
	stv_g.scale = new_scale;
	stv_g.vsc = s.p_v.scale
	stv_g.dy = new_dy;
	stv_g.st = st
	stv_g.v = s.v;
	set_g()
}

// -- set the staff output buffer and scale when delayed output
function set_dscale(st, no_scale) {
	if (output) {
︙︙
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
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	// staff scale only
}
Abc.prototype.sy = sy;
Abc.prototype.sh = function(h) {
	if (stv_g.st < 0)
		return h / stv_g.scale
	return h







|







532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
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) / stv_g.vsc
	return stv_g.dy - y	// staff scale only
}
Abc.prototype.sy = sy;
Abc.prototype.sh = function(h) {
	if (stv_g.st < 0)
		return h / stv_g.scale
	return h
︙︙
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
					x, y, tgl.sc, tgl.c);
			} else {
//				out_XYAB('<text x="X" y="Y">A</text>\n', x, y, tgl.c)
				gla[0].push(sx(x))
				gla[1].push(sy(y))
				gla[2] += tgl.c
			}
		} else {
			error(1, null, 'no definition of $1', gl)
		}
	}
}
// - specific functions -
// gua gda (acciaccatura)
function out_acciac(x, y, dx, dy, up) {







|







685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
					x, y, tgl.sc, tgl.c);
			} else {
//				out_XYAB('<text x="X" y="Y">A</text>\n', x, y, tgl.c)
				gla[0].push(sx(x))
				gla[1].push(sy(y))
				gla[2] += tgl.c
			}
		} else if (gl != 'nil') {
			error(1, null, 'no definition of $1', gl)
		}
	}
}
// - specific functions -
// gua gda (acciaccatura)
function out_acciac(x, y, dx, dy, up) {
︙︙
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923

function out_deco_str(x, y, de) {
    var	name = de.dd.glyph			// class

	if (name == 'fng') {
		out_XYAB('\
<text x="X" y="Y" style="font-size:14px">A</text>\n',
			x - 2, y, m_gl(de.dd.str))
		return
	}

	if (name == '@') {			// compatibility
		name = 'at'
	} else if (!/^[A-Za-z][A-Za-z\-_]*$/.test(name)) {
		error(1, de.s, "No function for decoration '$1'", de.dd.name)







|







914
915
916
917
918
919
920
921
922
923
924
925
926
927
928

function out_deco_str(x, y, de) {
    var	name = de.dd.glyph			// class

	if (name == 'fng') {
		out_XYAB('\
<text x="X" y="Y" style="font-size:14px">A</text>\n',
			x - 2, y + 1, m_gl(de.dd.str))
		return
	}

	if (name == '@') {			// compatibility
		name = 'at'
	} else if (!/^[A-Za-z][A-Za-z\-_]*$/.test(name)) {
		error(1, de.s, "No function for decoration '$1'", de.dd.name)
︙︙
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
			}
		}
		de1.x -= 8			// (there is no note width)
	}
	deco_l_tb[name](x, y, de)
}

// return a tempo note
function tempo_note(s, dur) {
    var	p,
	elts = identify_note(s, dur)

	switch (elts[0]) {		// head
	case C.OVAL:
		p = "\ueca2"
		break







|
|







1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
			}
		}
		de1.x -= 8			// (there is no note width)
	}
	deco_l_tb[name](x, y, de)
}

// add a tempo note in 'str' and return its number of characters
function tempo_note(str, s, dur, dy) {
    var	p,
	elts = identify_note(s, dur)

	switch (elts[0]) {		// head
	case C.OVAL:
		p = "\ueca2"
		break
︙︙
1214
1215
1216
1217
1218
1219
1220
1221




1222

1223
1224
1225
1226
1227
1228
1229
1230
			break
		default:
			p = "\ueca5"
			break
		}
		break
	}
	if (elts[1])			// dot




		p += '<tspan dx=".1em">\uecb7</tspan>'

	return p
} // tempo_note()

// build the tempo string
function tempo_build(s) {
    var	i, j, bx, p, wh, dy,
	w = 0,
	str = []







|
>
>
>
>
|
>
|







1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
			break
		default:
			p = "\ueca5"
			break
		}
		break
	}
	str.push('<tspan\nclass="' +
			font_class(cfmt.musicfont) +
		'" style="font-size:' +
		(gene.curfont.size * 1.3).toFixed(1) + 'px"' +
		dy + '>' +
		p + '</tspan>'
		+ (elts[1] ? '\u2009.' : ''))		// dot
	return elts[1] ? 2 : 1
} // tempo_note()

// build the tempo string
function tempo_build(s) {
    var	i, j, bx, p, wh, dy,
	w = 0,
	str = []
︙︙
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
	if (s.tempo_str1) {
		str.push(s.tempo_str1)
		w += strwh(s.tempo_str1)[0]
	}
	if (s.tempo_notes) {
		dy = ' dy="-1"'			// notes a bit higher
		for (i = 0; i < s.tempo_notes.length; i++) {
			p = tempo_note(s, s.tempo_notes[i])
			str.push('<tspan\nclass="' +
					font_class(cfmt.musicfont) +
				'" style="font-size:' +
				(gene.curfont.size * 1.3).toFixed(1) + 'px"' +
				dy + '>' +
				p + '</tspan>')
			j = p.length > 1 ? 2 : 1	// (note and optional dot)
			w += j * gene.curfont.swfac
			dy = ''
		}
		str.push('<tspan dy="1">=</tspan>')
		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) {			// with a number of beats per minute
			str.push(s.tempo)
			w += strwh(s.tempo.toString())[0]
		} else {			// with a beat as a note
			p = tempo_note(s, s.new_beat)
			str.push('<tspan\nclass="' +
					font_class(cfmt.musicfont) +
				'" style="font-size:' +
				(gene.curfont.size * 1.3).toFixed(1) +
				'px" dy="-1">' +
				p + '</tspan>')
			j = p.length > 1 ? 2 : 1
			w += j * gene.curfont.swfac
			dy = 'y'
		}
	}
	if (s.tempo_str2) {
		if (dy)
			str.push('<tspan\n\tdy="1">' +







|
<
<
<
<
<
<
<














|
<
<
<
<
<
<
<







1250
1251
1252
1253
1254
1255
1256
1257







1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272







1273
1274
1275
1276
1277
1278
1279
	if (s.tempo_str1) {
		str.push(s.tempo_str1)
		w += strwh(s.tempo_str1)[0]
	}
	if (s.tempo_notes) {
		dy = ' dy="-1"'			// notes a bit higher
		for (i = 0; i < s.tempo_notes.length; i++) {
			j = tempo_note(str, s, s.tempo_notes[i], dy)







			w += j * gene.curfont.swfac
			dy = ''
		}
		str.push('<tspan dy="1">=</tspan>')
		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) {			// with a number of beats per minute
			str.push(s.tempo)
			w += strwh(s.tempo.toString())[0]
		} else {			// with a beat as a note
			j = tempo_note(str, s, s.new_beat, ' dy="-1"')







			w += j * gene.curfont.swfac
			dy = 'y'
		}
	}
	if (s.tempo_str2) {
		if (dy)
			str.push('<tspan\n\tdy="1">' +
︙︙
1323
1324
1325
1326
1327
1328
1329














1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347


1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359

1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
	s.invis = true
} // writempo()

// update the vertical offset
function vskip(h) {
	posy += h
}















// create the SVG image of the block
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 = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
	xmlns:xlink="http://www.w3.org/1999/xlink"\n\
	fill="currentColor" stroke-width=".7"',
	g = ''

	glout()



	font = get_font("music")
	head += ' class="' + font_class(font) +
		' tune' + tunes.length + '"\n'	// tune index for play

	posy *= cfmt.scale
	if (user.imagesize != undefined)
		head += user.imagesize
	else
		head += ' width="' + w
			+ 'px" height="' + posy.toFixed(0) + 'px"'
	head += ' viewBox="0 0 ' + w + ' '
		+ posy.toFixed(0) + '"'

	if (cfmt.fgcolor || cfmt.bgcolor)
		head += ' style="'
			+ (cfmt.fgcolor ? ('color:' + cfmt.fgcolor + ';')
					: '')
			+ (cfmt.bgcolor ? ('background-color:' + cfmt.bgcolor)
					: '')
			+ '"'
	head += '>\n' + fulldefs

	if (style || font_style)
		head += '<style>' + font_style + style + '\n</style>\n'

	if (defs)
		head += '<defs>' + defs + '\n</defs>\n'








>
>
>
>
>
>
>
>
>
>
>
>
>
>



|














>
>











|
>
|
|
<
<
|
<
<
<







1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374


1375



1376
1377
1378
1379
1380
1381
1382
	s.invis = true
} // writempo()

// update the vertical offset
function vskip(h) {
	posy += h
}

// clear the styles
function clr_sty() {
	font_style = ''
	if (cfmt.fullsvg) {
		defined_glyph = {}
		for (var i = 0; i < abc2svg.font_tb.length; i++)
			abc2svg.font_tb[i].used = 0 //false
		ff.used = 0 //false		// clear the font-face
	} else {
		style =
			fulldefs = ''
	}
} // clr_sty()

// create the SVG image of the block
function svg_flush() {
	if (multicol || !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 = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
	xmlns:xlink="http://www.w3.org/1999/xlink"\n\
	fill="currentColor" stroke-width=".7"',
	g = ''

	glout()

	if (cfmt.fgcolor)
		head += ' color="' + cfmt.fgcolor + '"'
	font = get_font("music")
	head += ' class="' + font_class(font) +
		' tune' + tunes.length + '"\n'	// tune index for play

	posy *= cfmt.scale
	if (user.imagesize != undefined)
		head += user.imagesize
	else
		head += ' width="' + w
			+ 'px" height="' + posy.toFixed(0) + 'px"'
	head += ' viewBox="0 0 ' + w + ' '
		+ posy.toFixed(0) + '">\n'
	head += fulldefs
	if (cfmt.bgcolor)
		head += '<rect width="100%" height="100%" fill="'


			+ cfmt.bgcolor + '"/>\n'




	if (style || font_style)
		head += '<style>' + font_style + style + '\n</style>\n'

	if (defs)
		head += '<defs>' + defs + '\n</defs>\n'

︙︙
1382
1383
1384
1385
1386
1387
1388


1389
1390
1391
1392
1393



1394
1395


1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
		g = '</g>\n'
	}

	if (psvg)			// if PostScript support
		psvg.ps_flush(true);	// + setg(0)

	// start a block if needed


	if (blkdiv > 0) {
		user.img_out(blkdiv == 1 ?
			'<div class="nobrk">' :
			'<div class="nobrk newpage">')
		blkdiv = -1		// block started



	}
	user.img_out(head + output + g + "</svg>");


	output = ""

	font_style = ''
	if (cfmt.fullsvg) {
		defined_glyph = {}
		for (i = 0; i < abc2svg.font_tb.length; i++)
			abc2svg.font_tb[i].used = false
	} else {
		style = '';
		fulldefs = ''
	}
	defs = '';
	posy = 0
	img.wx = 0			// space used between the margins
}

// mark the end of a <div> block
function blk_flush() {
	svg_flush()
	if (blkdiv < 0 && (!parse.state || cfmt.splittune)) {
		user.img_out('</div>')
		blkdiv = 0
	}
}
Abc.prototype.blk_flush = blk_flush







>
>





>
>
>


>
>


|
<
<
<
<
<
<
<
<








|





1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413








1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
		g = '</g>\n'
	}

	if (psvg)			// if PostScript support
		psvg.ps_flush(true);	// + setg(0)

	// start a block if needed
	if (parse.state == 1 && user.page_format && !blkdiv)
		blkdiv = 1		// new tune
	if (blkdiv > 0) {
		user.img_out(blkdiv == 1 ?
			'<div class="nobrk">' :
			'<div class="nobrk newpage">')
		blkdiv = -1		// block started
	} else if (blkdiv < 0 && cfmt.splittune) {
		i = 1			// header and first music line
		blkdiv = 0
	}
	user.img_out(head + output + g + "</svg>");
	if (i)
		user.img_out("</div>")
	output = ""

	clr_sty()








	defs = '';
	posy = 0
	img.wx = 0			// space used between the margins
}

// mark the end of a <div> block
function blk_flush() {
	svg_flush()
	if (blkdiv < 0 && !parse.state) {
		user.img_out('</div>')
		blkdiv = 0
	}
}
Abc.prototype.blk_flush = blk_flush
Changes to core/tail.js.
︙︙
22
23
24
25
26
27
28

29
30
31
32
33
34
35

// Abc functions used by the modules
Abc.prototype.a_de = function() { return a_de }
Abc.prototype.add_style = function(s) { style += s };
Abc.prototype.anno_a = anno_a
Abc.prototype.cfmt = function() { return cfmt };
Abc.prototype.clone = clone;

Abc.prototype.deco_put = function(nm, s) {
	a_dcn.push(nm)
	deco_cnv(s)
}
Abc.prototype.defs_add = defs_add
Abc.prototype.dh_put = function(nm, s, nt) {
	a_dcn.push(nm)







>







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

// Abc functions used by the modules
Abc.prototype.a_de = function() { return a_de }
Abc.prototype.add_style = function(s) { style += s };
Abc.prototype.anno_a = anno_a
Abc.prototype.cfmt = function() { return cfmt };
Abc.prototype.clone = clone;
Abc.prototype.clr_sty = clr_sty
Abc.prototype.deco_put = function(nm, s) {
	a_dcn.push(nm)
	deco_cnv(s)
}
Abc.prototype.defs_add = defs_add
Abc.prototype.dh_put = function(nm, s, nt) {
	a_dcn.push(nm)
︙︙
71
72
73
74
75
76
77

78
79
80
81

82
83
84
85
86
87
88
Abc.prototype.out_deco_val = out_deco_val;
Abc.prototype.out_ltr = out_ltr;
Abc.prototype.param_set_font = param_set_font;
Abc.prototype.parse = parse;
Abc.prototype.psdeco = empty_function;
Abc.prototype.psxygl = empty_function;
Abc.prototype.set_cur_sy = function(sy) { cur_sy = sy };

Abc.prototype.set_dscale = set_dscale;
Abc.prototype.set_font = set_font;
Abc.prototype.set_a_gch = function(s, a) { a_gch = a; csan_add(s) }
Abc.prototype.set_hl = set_hl

Abc.prototype.set_page = set_page
Abc.prototype.set_pagef = function() { blkdiv = 1 }
Abc.prototype.set_realwidth = function(v) { realwidth = v }
Abc.prototype.set_scale = set_scale;
Abc.prototype.set_sscale = set_sscale
Abc.prototype.set_tsfirst = function(s) { tsfirst = s };
Abc.prototype.set_v_param = set_v_param;







>




>







72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
Abc.prototype.out_deco_val = out_deco_val;
Abc.prototype.out_ltr = out_ltr;
Abc.prototype.param_set_font = param_set_font;
Abc.prototype.parse = parse;
Abc.prototype.psdeco = empty_function;
Abc.prototype.psxygl = empty_function;
Abc.prototype.set_cur_sy = function(sy) { cur_sy = sy };
Abc.prototype.set_curvoice = function(p_v) { curvoice = p_v }
Abc.prototype.set_dscale = set_dscale;
Abc.prototype.set_font = set_font;
Abc.prototype.set_a_gch = function(s, a) { a_gch = a; csan_add(s) }
Abc.prototype.set_hl = set_hl
Abc.prototype.set_map = set_map
Abc.prototype.set_page = set_page
Abc.prototype.set_pagef = function() { blkdiv = 1 }
Abc.prototype.set_realwidth = function(v) { realwidth = v }
Abc.prototype.set_scale = set_scale;
Abc.prototype.set_sscale = set_sscale
Abc.prototype.set_tsfirst = function(s) { tsfirst = s };
Abc.prototype.set_v_param = set_v_param;
︙︙
Changes to core/tune.js.
︙︙
174
175
176
177
178
179
180

181
182
183
184
185
186
187
		}

		// if the previous symbol is a grace note at the same offset as the bar
		// remove the grace notes from the previous time sequence
		if (!fl) {
			while (prev.type == C.GRACE
			    && vtb[prev.v] && !vtb[prev.v].bar_type) {

				vtb[prev.v] = prev
				prev = prev.ts_prev
				fl = 1 //true
			}
		}

		if (v == undefined)







>







174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
		}

		// if the previous symbol is a grace note at the same offset as the bar
		// remove the grace notes from the previous time sequence
		if (!fl) {
			while (prev.type == C.GRACE
			    && vtb[prev.v] && !vtb[prev.v].bar_type) {
				delete prev.seqst
				vtb[prev.v] = prev
				prev = prev.ts_prev
				fl = 1 //true
			}
		}

		if (v == undefined)
︙︙
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
					s.st = s2.st
					break
				}
			}
		}
	} // ins_pq()

	// do the note mapping stuff
	function do_map(s) {
	    var	m, nt

		for (m = 0; m <= s.nhd; m++) {
			nt = s.notes[m]
			set_map(s.p_v, nt, nt.acc, 1)		// transpose done
		}
	} // do_map()

	// set the duration of the notes under a feathered beam
	function set_feathered_beam(s1) {
		var	s, s2, t, d, b, i, a,
			d = s1.dur,
			n = 1

		/* search the end of the beam */







<
<
<
<
<
<
<
<
<
<







431
432
433
434
435
436
437










438
439
440
441
442
443
444
					s.st = s2.st
					break
				}
			}
		}
	} // ins_pq()











	// set the duration of the notes under a feathered beam
	function set_feathered_beam(s1) {
		var	s, s2, t, d, b, i, a,
			d = s1.dur,
			n = 1

		/* search the end of the beam */
︙︙
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
					}
					break
				}
				continue
			case C.NOTE:
				if (s.feathered_beam)
					set_feathered_beam(s)
				if (p_voice.map
				 && maps[p_voice.map])
					do_map(s)
				break
			}
		}
	}
}

/* -- create a new staff system -- */







<
<
<







559
560
561
562
563
564
565



566
567
568
569
570
571
572
					}
					break
				}
				continue
			case C.NOTE:
				if (s.feathered_beam)
					set_feathered_beam(s)



				break
			}
		}
	}
}

/* -- create a new staff system -- */
︙︙
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
}

// convert a note to ABC
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







|

|







748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
}

// convert a note to ABC
function not2abc(pit, acc) {
    var	i,
	nn = ''

	if (acc) {
		if (typeof acc != "object") {
			nn = ['__', '_', '', '^', '^^', '='][acc + 2]
		} else {
			i = acc[0]
			if (i > 0) {
				nn += '^'
			} else {
				nn += '_'
				i = -i
︙︙
797
798
799
800
801
802
803
804
805
806
807
808

809
810
811
812
813
814
815
816
817
818
819
820
	a = text.split(/\s+/)

	if (a.length < 3) {
		syntax(1, errs.not_enough_p)
		return
	}
	ns = a[1]
	if (ns[0] == '*' || ns.indexOf("all") == 0) {
		ns = 'all'
	} else {
		if (ns.indexOf("octave,") == 0	// remove the octave part
		 || 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







<
|
<
|
|
>

|
<
<
<







785
786
787
788
789
790
791

792

793
794
795
796
797



798
799
800
801
802
803
804
	a = text.split(/\s+/)

	if (a.length < 3) {
		syntax(1, errs.not_enough_p)
		return
	}
	ns = a[1]

	if (ns != '*') {

 		if (ns.indexOf("octave,") == 0	// remove the octave part
		 || ns.indexOf("key,") == 0
		 || !ns.indexOf("tonic,")) {
			ty = ns[0]
			ns = ns.split(',')[1].toUpperCase()



		}
		tmp = new scanBuf
		tmp.buffer = ns
		note = parse_acc_pit(tmp)
		if (!note) {
			syntax(1, "Bad note in %%map")
			return
︙︙
1281
1282
1283
1284
1285
1286
1287
1288










1289
1290
1291
1292
1293
1294
1295
				break
			i = text.indexOf('>', i)
			j = text.indexOf('</style>', i)
			if (j < 0) {
				syntax(1, "No </style> in %%beginsvg sequence")
				break
			}
			style += text.slice(i + 1, j).replace(/\s+$/, '')










		}
		j = 0
		while (1) {
			i = text.indexOf('<defs>\n', j)
			if (i < 0)
				break
			j = text.indexOf('</defs>', i)







|
>
>
>
>
>
>
>
>
>
>







1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
				break
			i = text.indexOf('>', i)
			j = text.indexOf('</style>', i)
			if (j < 0) {
				syntax(1, "No </style> in %%beginsvg sequence")
				break
			}
			s = text.slice(i + 1, j).replace(/\s+$/gm, '')
			if (cfmt.fullsvg) {
				i = s.match(/@font-face[^}]*}/)
				if (i && i[0].indexOf("text") > 0) {
					ff.text = "\n"
						+ i[0]	// assume only one @font-face
					s = s.replace(i[0], '')
				}
			}
			if (s && s != "\n")
				style += s
		}
		j = 0
		while (1) {
			i = text.indexOf('<defs>\n', j)
			if (i < 0)
				break
			j = text.indexOf('</defs>', i)
︙︙
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528




1529
1530
1531
1532
1533
1534
1535
1536
1537









1538
1539
1540
1541
1542

1543
1544
1545
1546
1547
1548

1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
	sym_link(s2)

	curvoice = p_v_sav
} // fill_mr_ba()

/* -- get staves definition (%%staves / %%score) -- */
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 sequence with many voices, load the other voices
	if (curvoice && curvoice.clone) {
		i = parse.eol
		parse.eol = parse.bol		// remove the %%staves line
		do_cloning()
		parse.eol = i
	}

	if (parm) {
		a_vf = parse_staves(parm)	// => array of [vid, flags]
		if (!a_vf)
			return
	} else if (staves_found < 0) {
		syntax(1, errs.bad_val, '%%' + cmd)
		return
	}

	/* create a new staff system */
	for (v = 0; v < nv; v++) {
		p_voice = voice_tb[v]




		if (p_voice.time > maxtime)
			maxtime = p_voice.time
	}
	if (!maxtime) {				// if first %%staves
		par_sy.staves = []
		par_sy.voices = []
	} else {
//		if (nv)					// if many voices
		self.voice_adj(1)









		/*
		 * create a new staff system and
		 * link the 'staves' symbol in a voice which is seen from
		 * the previous system - see sort_all
		 */

		for (v = 0; v < par_sy.voices.length; v++) {
			if (par_sy.voices[v]) {
				curvoice = voice_tb[v]
				break
			}
		}

		curvoice.time = maxtime;
		s = {
			type: C.STAVES,
			dur: 0
		}

		sym_link(s);		// link the staves in this voice
		par_sy.nstaff = nstaff;

		// if no parameter, duplicate the current staff system
		// and do a voice re-synchronization
		if (!parm) {
			s.sy = clone(par_sy, 1)
			par_sy.next = s.sy
			par_sy = s.sy
			staves_found = maxtime
			for (v = 0; v < nv; v++)
				voice_tb[v].time = maxtime
			curvoice = voice_tb[par_sy.top_voice]
			return
		}

		new_syst();
		s.sy = par_sy
	}







|






|
|

|














>
>
>
>









>
>
>
>
>
>
>
>
>





>






>
|









<

|



<
<







1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567

1568
1569
1570
1571
1572


1573
1574
1575
1576
1577
1578
1579
	sym_link(s2)

	curvoice = p_v_sav
} // fill_mr_ba()

/* -- get staves definition (%%staves / %%score) -- */
function get_staves(cmd, parm) {
    var	s, p_voice, p_voice2, i, flags, v, vid, a_vf, eoln,
	st, range,
	nv = voice_tb.length,
	maxtime = 0

	// if sequence with many voices, load the other voices
	if (curvoice && curvoice.clone) {
//		i = parse.eol
//		parse.eol = parse.bol		// remove the %%staves line
		do_cloning()
//		parse.eol = i
	}

	if (parm) {
		a_vf = parse_staves(parm)	// => array of [vid, flags]
		if (!a_vf)
			return
	} else if (staves_found < 0) {
		syntax(1, errs.bad_val, '%%' + cmd)
		return
	}

	/* create a new staff system */
	for (v = 0; v < nv; v++) {
		p_voice = voice_tb[v]
		if (p_voice.eoln) {
			eoln = 1
			delete p_voice.eoln
		}
		if (p_voice.time > maxtime)
			maxtime = p_voice.time
	}
	if (!maxtime) {				// if first %%staves
		par_sy.staves = []
		par_sy.voices = []
	} else {
//		if (nv)					// if many voices
		self.voice_adj(1)

		// synchronize the voices
		for (v = 0; v < nv; v++) {
			p_voice = voice_tb[v]
			p_voice.time = maxtime
			p_voice.lyric_restart = p_voice.last_sym
			p_voice.sym_restart = p_voice.last_sym
		}

		/*
		 * create a new staff system and
		 * link the 'staves' symbol in a voice which is seen from
		 * the previous system - see sort_all
		 */
	   if (!par_sy.voices[curvoice.v])
		for (v = 0; v < par_sy.voices.length; v++) {
			if (par_sy.voices[v]) {
				curvoice = voice_tb[v]
				break
			}
		}

		curvoice.eoln = eoln
		s = {
			type: C.STAVES,
			dur: 0
		}

		sym_link(s);		// link the staves in this voice
		par_sy.nstaff = nstaff;

		// if no parameter, duplicate the current staff system

		if (!parm) {
			s.sy = clone(par_sy, 2)		// clone the staves and voices
			par_sy.next = s.sy
			par_sy = s.sy
			staves_found = maxtime


			curvoice = voice_tb[par_sy.top_voice]
			return
		}

		new_syst();
		s.sy = par_sy
	}
︙︙
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
			p_voice.last_sym = s	// set back the last symbol
		}
	}
	range = 0
	for (i = 0; i < a_vf.length; i++) {
		vid = a_vf[i][0];
		p_voice = new_voice(vid);
		p_voice.time = maxtime;
		v = p_voice.v

		a_vf[i][0] = p_voice;

		// set the range and add the overlay voices
		while (1) {
			par_sy.voices[v] = {







<







1595
1596
1597
1598
1599
1600
1601

1602
1603
1604
1605
1606
1607
1608
			p_voice.last_sym = s	// set back the last symbol
		}
	}
	range = 0
	for (i = 0; i < a_vf.length; i++) {
		vid = a_vf[i][0];
		p_voice = new_voice(vid);

		v = p_voice.v

		a_vf[i][0] = p_voice;

		// set the range and add the overlay voices
		while (1) {
			par_sy.voices[v] = {
︙︙
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669

1670
1671
1672
1673
1674
1675
1676
		if (flags & FL_VOICE) {
			p_voice.floating = true;
			p_voice.second = true
		} else {
			st++;
			if (!par_sy.staves[st]) {
				par_sy.staves[st] = {
					stafflines: p_voice.stafflines || "|||||",
					staffscale: 1
				}
			}

			par_sy.staves[st].flags = 0
		}
		v = p_voice.v;
		p_voice.st = p_voice.cst =
				par_sy.voices[v].st = st;
		par_sy.staves[st].flags |= flags
		if (flags & OPEN_PARENTH) {







<



>







1664
1665
1666
1667
1668
1669
1670

1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
		if (flags & FL_VOICE) {
			p_voice.floating = true;
			p_voice.second = true
		} else {
			st++;
			if (!par_sy.staves[st]) {
				par_sy.staves[st] = {

					staffscale: 1
				}
			}
			par_sy.staves[st].stafflines = p_voice.stafflines || "|||||",
			par_sy.staves[st].flags = 0
		}
		v = p_voice.v;
		p_voice.st = p_voice.cst =
				par_sy.voices[v].st = st;
		par_sy.staves[st].flags |= flags
		if (flags & OPEN_PARENTH) {
︙︙
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
		if (p_voice.id == id)
			return p_voice		// old voice
	}

	p_voice = {
		v: v,
		id: id,
		time: 0,
		new: true,
		pos: {
//			dyn: 0,
//			gch: 0,
//			gst: 0,
//			orn: 0,
//			stm: 0,







|







2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
		if (p_voice.id == id)
			return p_voice		// old voice
	}

	p_voice = {
		v: v,
		id: id,
		time: staves_found >= 0 ? staves_found : 0,
		new: true,
		pos: {
//			dyn: 0,
//			gch: 0,
//			gst: 0,
//			orn: 0,
//			stm: 0,
︙︙
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
// treat V: with many voices
function do_cloning() {
    var	i,
	clone = curvoice.clone,
	vs = clone.vs,
	a = clone.a,
	bol = clone.bol,
	eol = parse.eol,
	parse_sav = parse,
	file = parse.file

	delete curvoice.clone

	if (file[eol - 1] == '[')	// if stop on [V:xx]
		eol--







|







2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
// treat V: with many voices
function do_cloning() {
    var	i,
	clone = curvoice.clone,
	vs = clone.vs,
	a = clone.a,
	bol = clone.bol,
	eol = parse.bol,
	parse_sav = parse,
	file = parse.file

	delete curvoice.clone

	if (file[eol - 1] == '[')	// if stop on [V:xx]
		eol--
︙︙
2241
2242
2243
2244
2245
2246
2247


2248
2249
2250
2251
2252
2253
2254
// change state from 'tune header' to 'in tune body'
// curvoice is defined when called from get_voice()
function goto_tune() {
    var	v, p_voice

	set_page();
	write_heading();


	if (glovar.new_nbar) {
		gene.nbar = glovar.new_nbar	// measure numbering
		glovar.new_nbar = 0
	} else {
		gene.nbar = 1
	}








>
>







2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
// change state from 'tune header' to 'in tune body'
// curvoice is defined when called from get_voice()
function goto_tune() {
    var	v, p_voice

	set_page();
	write_heading();
	blk_flush()				// tune heading in a specific SVG

	if (glovar.new_nbar) {
		gene.nbar = glovar.new_nbar	// measure numbering
		glovar.new_nbar = 0
	} else {
		gene.nbar = 1
	}

︙︙
Changes to editor/edit.js.
1
2
3
4
5
6
7
8
9
10
// 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.


|







1
2
3
4
5
6
7
8
9
10
// edit.js - file used in the abc2svg editor
//
// Copyright (C) 2014-2024 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.
︙︙
41
42
43
44
45
46
47

48
49
50
51
52
53
54
	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')







>







41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
	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
	tunes,				// tune list, updated on rendering refresh
	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')
︙︙
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
	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";







|
<
|
<







158
159
160
161
162
163
164
165

166

167
168
169
170
171
172
173
	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)

		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";
︙︙
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
//	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







|
<







533
534
535
536
537
538
539
540

541
542
543
544
545
546
547
//	1: Selection
//	2: Loop
//	3: Continue
function play_tune(what) {
	if (!abc)
		return			// no generation yet
    var	i, si, ei, elt,
	C = abc2svg.C


	if (play.playing) {
		if (!play.stop) {
			play.stop = -1;
			play.abcplay.stop()
		}
		return
︙︙
657
658
659
660
661
662
663
664

665
666
667

668
669
670
671
672
673
674
675
676
677
		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
	}







|
>


<
>
|
<
<







655
656
657
658
659
660
661
662
663
664
665

666
667


668
669
670
671
672
673
674
		play.abcplay.play(si, ei, play.repv)
	}

	// play tune()
	ctxMenu.style.display = "none";	// remove the play menu

	play.playing = true;
	if (tunes != abc.tunes) {		// if new display
		tunes = abc.tunes

		// generate the play data of all tunes

		for (i = 0; i < tunes.length; i++) {
			elt = tunes[i]


			play.abcplay.add(elt[0], elt[1], elt[3])
		}

		play.si = play.ei = null
		play.stop = 0
		play.loop = false
	}
︙︙
695
696
697
698
699
700
701
702

703
704
705
706

707

708




709
710
711
712
713
714
715
		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







|
>
|
|
|

>
|
>
|
>
>
>
>







692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
		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.getAttribute('class')
		si = /tune(\d*)/.exec(si)
		if (!si) {
			play.playing = 0 //false
			return			// no tune here
		}
		si = tunes[si[1]][0]		// first symbol of the tune
		ei = si
		while (ei && !ei.dur)
			ei = ei.ts_next
		if (!ei) {
			play.playing = 0 //false
			return			// nothing to play
		}
		ei = null
	}

	if (what != 3) {		// if not continue
		play.si = si;
		play.ei = ei;
		play.loop = what == 2
︙︙
866
867
868
869
870
871
872










873
874
875
876
877
878
879

		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







>
>
>
>
>
>
>
>
>
>







870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893

		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

	// create a hidden span for string width computation
	e = document.createElement("span")
	e.style.position = "absolute"
	e.style.top =
		e.style.padding = 0
	e.style.visibility = "hidden"
	e.style.lineHeight = 1
	document.body.appendChild(e)
	abc2svg.el = e
}

// drag and drop
function drag_enter(evt) {
	evt.stopImmediatePropagation();
	evt.preventDefault()	// allow drop
//fixme: "move" does not work
︙︙
Changes to font.js.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25


26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50




51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92


93
94
95
96
97
98
99
100
101
102


103
104
105
106
107


108
109
110

111
112
113
114
115
116
117
118
119
120
121



122
123
124
125
126
127
128
129
130
131

132

133
134
135
136
137
138
139
140
141


142

143
144
145
146

147

148
149

150
151
152
153
154
155
156
157
158
159
160
161
162
163
164

165
166
167
168
169
170
171
172

173
174
175

176
177
178
179
180
181
182
183
184
185
186
187






188
189
190
191
192
193
194
195
196
197
198

199
200
201
202
203



204
205

206

207
208
209
210
211
212
213
214
215
216
217
218
219




220
221
222
223
224
225
226


227
228
229
230
231
232
233


234
235


236
237
238
239

240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256

257



258
259
260
261
262

263
264
265
266
267
268
269
270

271
272
273
274
275
276
277
278
279
280
281




282

283
284

285
286
287
288
289
290

291

292
293
294
295
296
297
298


299
300
301

302
303
304
305
306
307
308
309






310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334








335
336
337
338
339
340
341
342
343
344
345
346
347
348

349
350
351
352


353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
// abc2svg music font
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\




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\
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\
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\
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\


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\
AwGbFGk0IDAXExIXEgoZBwwKBwgFBgMGATYUFD8hdl8gMBcTEhcSChkLEA8LDQoPDRTmxcEmIBoi\
FA8OCwcNDgYREyUkPDdVJxUTS18mIBoiFA8OCwYccwwGAUtfJiAaIhQPDgsGHAULChQNGg0eBijF\
wSYgGiIUDw4LBw0OBhETJSQ8N1UAAAAB/37/YAN0AbgAqgAAEzM+ATMyFhUUBiMiJjU0NzY0IyIH\
BgcXPgEzMhYVFAYjIiY1NDc2NCMiDgcHMzIUKwEOASMiJjU0NjMyFhUUBwYVFDMyPgc3Iw4BIyIm\
NTQ2MzIWFRQHBhUUMzI+BzcjDgEjIiY1NDYzMhYVFAcGFRQzMj4HNyMiNTQ7AT4BMzIWFRQGIyIm\
NTQ3NjQjIg4CBwbMnBRpNCAwFxMSFxIKGSYXAwGbFGk0IDAXExIXEgoZBwwKBwgFBgMGATYTEz8h\
dl8gMBcTEhcSChkLEA8LDQoPDRQKmyF2XyAwFxMSFxIKGQsQDwsNCg4NFQqbIXZfIDAXExIXEgoZ\




CxAPCw0KDg0UCjUTEUEUaTQgMBcTEhcSChkOFQ4IBgIBDktfJiAaIhQPDgsGHHMMBgFLXyYgGiIU\

Dw4LBhwFCwoUDRoNHgYoxcEmIBoiFA8OCwcNDgYREyUkPDdVJ8XBJiAaIhQPDgsHDQ4GERMlJDs4\
VSfFwSYgGiIUDw4LBw0OBhETJSQ8N1UnFRNLXyYgGiIUDw4LBhwTJyIbCgAB/37/YASAAbgA4AAA\

ARc+ATMyFhUUBiMiJjU0NzY0IyIHBgcXPgEzMhYVFAYjIiY1NDc2NCMiDgcHMzIUKwEOASMiJjU0\
NjMyFhUUBwYVFDMyPgc3Iw4BIyImNTQ2MzIWFRQHBhUUMzI+BzcjDgEjIiY1NDYzMhYVFAcGFRQz\
Mj4HNyMOASMiJjU0NjMyFhUUBwYVFDMyPgc3IyI1NDsBPgEzMhYVFAYjIiY1NDc2NCMiBwYHFz4B\
MzIWFRQGIyImNTQ3NjQjIg4HAdicFGk0IDAXExIXEgoZJhcDAZsUaTQgMBcTEhcSChkHDAoHCAUG\
AwYBNhMTPyF2XyAwFxMSFxIKGQsQDwsNCg8NFAqbIXZfIDAXExIXEgoZCxAPCw0KDg0UCpshdl8g\
MBcTEhcSChkLEA8LDQoPDRQKmyF2XyAwFxMSFxIKGQsQDwsNCg4NFAo1ExFBFGk0IDAXExIXEgoZ\

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\
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")'


|
|
|
|
|
|
|

|
|
|






|
<
<
|
|
|
>
>
|
|
|
|
|
|
|
|
|



|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
|
|
|
<
<
<
<
|
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
<
|
>
>
|
|
|
|
|
|
|
|
|
|
>
>
|
|
<
<
|
>
>
|
<
|
>
|
|
|
|
|
|
|
<
<
<
|
>
>
>
|
|
|
|
<
<
|
<
|
<
>
|
>
|
|
|
|
<
<
<
|
|
>
>
|
>
|
|
|
|
>
|
>
|
|
>
|
|
|
<
<
<
<
<
|
|
|
|
|
|
<
>
|
|
|
|
|
|
|
<
>
<
|
|
>
|
|
|
|
<
<
<
<
<
<
|
|
>
>
>
>
>
>
|
|
|
|
|
<
|
|
|
|
|
>
|
<
|
|
|
>
>
>
|
|
>
|
>
|
<
|
<
<
|
|
<
|
<
<
|
|
>
>
>
>
|
<
<
|
<
<
|
>
>
|
<
<
|
|
|
|
>
>
|
<
>
>
|
<
|
<
>
|
<
|
<
<
<
<
<
<
<
|
|
|
|
|
|
<
>
|
>
>
>
|
|
<
|
|
>
|
|
|
|
|
|
<
|
>
|
|
|
|
|
<
|
|
|
|
|
>
>
>
>
|
>
|
|
>
|
|
<
|
|
|
>
|
>
|
|
<
<
<
|
|
>
>
|
<
<
>
|
<
|
<
<
<
|
|
>
>
>
>
>
>
|
<
|
|
<
<
<
<
<
|
|
|
|
|
|
|
|
<
<
<
|
|
|
|
|
>
>
>
>
>
>
>
>
|
<
|
<
<
|
<
<
<
<
<
|
|
|
>
|
<
|
|
>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
|
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20


21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57




58

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88


89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105


106
107
108
109

110
111
112
113
114
115
116
117
118



119
120
121
122
123
124
125
126


127

128

129
130
131
132
133
134
135



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154





155
156
157
158
159
160

161
162
163
164
165
166
167
168

169

170
171
172
173
174
175
176






177
178
179
180
181
182
183
184
185
186
187
188
189

190
191
192
193
194
195
196

197
198
199
200
201
202
203
204
205
206
207
208

209


210
211

212


213
214
215
216
217
218
219


220


221
222
223
224


225
226
227
228
229
230
231

232
233
234

235

236
237

238







239
240
241
242
243
244

245
246
247
248
249
250
251

252
253
254
255
256
257
258
259
260

261
262
263
264
265
266
267

268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283

284
285
286
287
288
289
290
291



292
293
294
295
296


297
298

299



300
301
302
303
304
305
306
307
308

309
310





311
312
313
314
315
316
317
318



319
320
321
322
323
324
325
326
327
328
329
330
331
332

333


334





335
336
337
338
339

340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365

366

367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
// abc2svg music font
var musicfont = 'url("data:application/octet-stream;base64,\
AAEAAAAOAIAAAwBgRkZUTZUzWF8AAFWEAAAAHEdERUYAFQAUAABVaAAAABxPUy8yWLldBQAAAWgA\
AABWY21hcHhDtwwAAAP4AAAD3mN2dCAAIgKIAAAH2AAAAARnYXNw//8AAwAAVWAAAAAIZ2x5Znhb\
i3cAAAj8AABDvGhlYWQXj7dYAAAA7AAAADZoaGVhCWn/FwAAASQAAAAkaG10eNC5+zgAAAHAAAAC\
OGxvY2FuelzOAAAH3AAAAR5tYXhwANUBEgAAAUgAAAAgbmFtZe6psCEAAEy4AAADFXBvc3QELFTy\
AABP0AAABY0AAQAAAAEAAOf5+OZfDzz1AAsEAAAAAADRlyIXAAAAAOJWUs3/OPzvBUsEiAAAAAgA\
AgAAAAAAAAABAAAEiPzvAFwEJf84/XQFSwABAAAAAAAAAAAAAAAAAAAAjgABAAAAjgDhAAUAAAAA\
AAIAAAABAAEAAABAAC4AAAAAAAEBewGQAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAACAAUDAAAA\
AAAAAAAAARAAAAAAAAAAAAAAAFBmRWQAQAAA7LcDOP84AFwEiAMRAAAAAQAAAAAAAAF2ACIAAAAA\
AVUAAAGQAAACWAAAAFcAAAAjAAAAJQAAACT//wBkAAAAZAAABCMAAAQlAAAB4P/cA7oAAAMLAAAC\
0gAAAr//ugHWAAADCwAAAw4AAAMn/8gAyAAAAWgAAAGuAAABIgAAAZAAAAF8AAABkAAAAZAAAAGB\
AAABkAAAAZAAAAGBAAABmQAJAfQAAAEEABQBBAAKAmsAJAISAAABwgAAAUIAAAFAAAABSv/+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\
9AAAAHgAAAAtAAACHAAAAPoAAAD6/+gBwgAAAUgAAAFAAAACCgAAAgoAAABkAAAAAAADAAAAAwAA\


ABwAAQAAAAAC2AADAAEAAAAcAAQCvAAAAGAAQAAFACAAAAAg4ADgMOA54EjgUOBc4GLgaeCM4JXg\
pOCp4LPhAeG74efiAOJJ4mTia+KD5KzkwOTR5OrlAeUx5TnlbeWC5dDl4uYY5iTmMOZQ5lXpFekg\
6SXpXeoC6qTsqey3//8AAAAAACDgAOAw4DjgQ+BQ4FzgYuBp4HrglOCg4Kngs+EB4bnh5+Hy4kDi\
YOJq4oDkoOTA5M7k4eUA5SDlOeVm5YLl0OXi5hDmJOYw5lDmVekR6SDpJeld6gLqpOyi7Lf//wAD\
/+QgBR/WAAAAAB+/H7Qfrx+pAAAAAAAAH4IfeR8sAAAeSQAAAAAAAAAAAAAAABuTAAAAAAAAAAAb\
NwAAGvQapxqWAAAaWRpOGi8aKwAAF2MXXxcoFoQV4wAAE9YAAQAAAAAAAAAAAFgAWgAAAAAAAAAA\
AFwAgACCAAAAAAAAAIQAAACGAKIAtAC8AL4AxAAAANoA4ADyAPQAAAEUAAAAAAAAARwAAAAAAAAA\
AAEkAAAAAAAAAAAAAAEiAAAAAAAHAAgACQAKAAsADAANAA4AEwAUABUAFgAXAAAAGAAZABoAGwAc\
AB0AHgAfACAAIQAiAAAAIwAkACUAJgAnACgAKQAqAC4AAAAvADEAAAAyAAAAAAAzAAAANAAAAAAA\
NQAAADYANwA4ADkAOgA7ADwAPQA+AD8AQABBAEIAQwBEAEUARgBHAEgASQBKAEsATABNAE4AAABP\
AAAAUAAAAAAAAABRAAAAAAAAAFIAVAAAAAAAVQBWAFcAWABZAFoAWwBcAF0AXgBfAGAAYQBiAGMA\
ZABlAGYAZwAAAAAAAABoAGkAagBrAGwAAABtAG4AbwBxAHIAAABzAAAAAAB0AHUAeQAAAHoAAAB7\
AAAAAAAAAHwAgQAAAAAAAACCAIgAiQAAAIoAAACLAAAAjAAAAQYAAAMAAAAAAAAAAQIAAAACAAAA\
AAAAAAAAAAAAAAAAAQAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAiAogAAAAqACoAKgA2AD4AbgB6AIYAkgCwAMIBNgGSAgICRgLSA2QDxAPY\
BHYFCAVgBaAGIgZEBlgGqgb6BxoHYAegB9oIJghmCKwIwAjmCQwJPglaCXwJpAm6CdQKCgoYCiQK\
MApCCl4KgAqOCqIKtArECxALIAs6C1QLjAvEDBIMXgzEDSYNpA4cDkIOXA6MDrYPDg8oD0IPaA/C\
D+IQIBBIEFwQaBB2EIYQrhDWEQ4RGhEmETIRPhFgEYYRvBIKEmoS3hMEEzIThhP6FE4UjhTSFSgW\
QhcaF7AYYhkkGbYaiBucHIgcyh0OHVgdch2WHaodwB3wHgAeEh4uHkQeaB6YH3QgMCBOIIAgkiCg\
ILwg1iD8IR4hTCFmIZAhzCHeAAAAAgAiAAABMgKqAAMABwAusQEALzyyBwQA7TKxBgXcPLIDAgDt\
MgCxAwAvPLIFBADtMrIHBgH8PLIBAgDtMjMRIREnMxEjIgEQ7szMAqr9ViICZgAAAQAAAAABkQGQ\
AAMAADERIREBkQGQ/nAAAQAAAAAAAAAAAAAAADEAAAEAAAAAAFcEAwAgAAARNTY1NCcmNTQ3BhUU\
FxYVFAcWFRQHBhUUFyY1NDc2NTQ1IxJXPxUlTU0lFT9XEiMCAwIYQzZgOTRmOjJLIjlhTWEYGGZM\
YDklSjI6ZjQ5YDZIAAEAAAAAACMD6AADAAARMxEjIyMD6PwYAAEAAAH0ACUD6AADAAARMxEjJSUD\
6P4MAAH//wNkACQEYAADAAARMwcjJAEkBGD8AAIAAAFAAGQCngAHAA8AABIiJjQ2MhYUAiImNDYy\
FhRHKh0dKh0dKh0dKh0COh0qHR0q/ukdKh0dKgAAAAEAAP/OAGQAMgAHAAAWIiY0NjIWFEcqHR0q\
HTIdKh0dKgAAAAUAAAAABCQBrgAvADcAPwBHAFMAACE1Mx4BMzI2NTQnLgQ1NDYzMhYXNzMXIy4B\
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\
NTQuAx4GJzM7OC4cEhISEhISEhMeBSk+SEAWCURNVhUBKT5IQRYJRE1WFQEpPkhBFglETVYVASk+\
SEEWCURNVhUBKT5IQf67A1cbOTM3Oj1IJSwrJy0sKyctLCsnLSwrKSs2ORkhN2RFNBirJFRDXSkD\
DQQ3ZUUzGaskVENdKQQMBDdlRTMZqyRUQ10pAw4DN2VFMxmrJFRDXSkEDAQ3ZUUzGQAABQAA/bwB\
PAJ5ACMAMAA9AEoAVwAAGQEzMj4CNTQnFhUUBxYVFAcWFRQHFhUUBxYVFA4FBzUyPgI1NCY1DgMn\
Mj4CNTQmNQ4DJzI+AjU0JjUOAycyPgI1NCY1DgMeHFVXPQUeExISEhISEhIcLjg7MycGHVVXPQEV\
Vk1ECR1VVz0BFVZNRAkdVVc9ARVWTUQJHVVXPQEVVk1E/bwDVylId0QhGTk2KykrLC0nKywtJyss\

LScrLCVIPTo3MzkbqilJeEQDDQQpXUNUhilJeEQDDQQpXUNUhilJeEQDDQQpXUNUhilJeEQDDQQp\

XUNUAAAAAgAA/2QA4QGwAAoAFgAANyIGHQE2NzY1NCY3MhYVFAcGIxEzETZnFCsiJisdByM5S1JE\
KCN6JhO5Dzg7LxsmJjIjSUxSAkz+vDQAAAIAAP6GAMUBegADAAwAABc3NQcRFTcRIzUHETcckJCp\
GawBZS2WLQFJ6DT9wOIzAkMBAAACAAD+mAD/AWgAAwAfAAA3FTc1AyM1BzU3NQc1NzUzFTc1MxU3\
FQcVNxUHFSM1B1NaWh41NTU1HlodNTU1NR1aRqcbp/43ow9cD6cPWg+onxyrow9cD6cPWg+onxwA\
AAABABT/hAELAHoAHgAAFzUmJwcwFSM1MzcnMCM1MxUWFzcwNTMVIwYHFzAzFcMoDDNIOTMzOUgh\
EjRIOSETNDl8OyYNMztKMjJIOSIRMzlHIhE0SAAEAAD/agFsAbAADgAcACsAOgAANw4BHQEyNzY3\
NjU0JyYjNzIWFRQHBgcGIxEzETYXDgEdATI3Njc2NTQnJiM3MhYVFAcGBw4BIxEzETZOER4OHh8M\






BAoQERkdKwkYKzUvHxnRER0QHh0LBgsQDxYfKgsZKBY3Fh4bfQEeEMYpKzQNGR4UFSY5IRIgOTRA\
Akb+wTImAR0RxikvMBMTHBYVJjYkFhw+LxslAkb+wTIAAAEAAP8GAIwA+gAOAAA3FQYVFBcVLgM0\
PgKMUFAaJTIbGzIl+hRDo6ZAFA8fOlpwWjofAAABAAD/BgCMAPoADgAANR4DFA4CBzU2NTQnGiUy\
GxsyJRpQUPoPHzpacFo6Hw8UQKajQwAAAgAy/2QBDQGwAAoAFgAANyIGFRQXFhc1NCYnMhcRMxEi\
JyY1NDarFyAuLhoqMzojIzpSTzl6JhswRUIDyxIeJjQBRP20UlBFIzIAAAT/9f9qAWwBsAAOAB0A\
LAA7AAA3IyIHBhUUFxYXFjM1NCYnMhcRMxEiJicmJyY1NDYXDgEdATI3Njc2NTQnJiM3MhYVFAcG\
Bw4BIxEzETZXAREQCgQMHx4OHiswGR8WNxcrGAkr6REdEB4dCwYLEA8WHyoLGSgWNxYeG30VFB4Z\
DTQrKcYQHicyAT/9uiUbNDkgEiE5JgEdEcYpLzATExwWFSY2JBYcPi8bJQJG/sEyAAAAAAEAAP7A\
AKkBQAATAAATMxU3FQcVNxUHFSM1BzU3NQc1N0QeR0dHRx5EREREAUCiDlwOfw5aD6iiDlwOfw5a\
DwADAAD+mAE6AWgAIwAnACsAADc1MxU3FQcVNxUHFSM1BxUjNQ8BIzUHNTc1BzU3NTMVNzUzFQM1\
BxU3FTc16R4zMzMzHj0ePAEeMzMzMx49Hh49Wz3AqJ4PXA+fD1oPtaoSrKQQqJ4PXA+fD1oPtaoS\
rKT+/Z4Rn7ieEZ8AAf/9AAABPwD0ABgAADcGIyImNTQ/ATYvASY1NDYzMjEXBRYVFAcSAgMHCQbP\
Dg7NCAsHAQIBHw4OAQEQCAoDSQcGTwMLChIBawYODQUAAAABAAAAAABQAFAACQAANTQ2MhYUBiMi\

JhciFxcREBgoERcXIhcYAAAAAQAAAAABVAAyAAMAADE1IRUBVDIyAAAAAQAAAAAAZAEYAAMAADMD\
MwMoKGQoARj+6AAAAAEAAAAAARgBNQAFAAAxGwEjJweMjEFYWgE1/svGxgAAAgAAAAACWAFKAA4A\
GQAAMTQ2MzIeAhUjLgEiBgchIiY0NjMyFhUUBrN5OWtVMw8LouCiCwEcFyUlFxkjI5iyLFGATW6G\
hm4kMCQkGBkjAAABAAAAAAC2AS0AGAAAEzIWFxYVFAcOASMwJyY1NDY1NCMuATU0NlYbGxAaMhlE\
EAYBRxQbKC0BLQwRHTA9PB0tAwECCGsTDwEmHB4xAAAAAAIABQAAAY4B9QARACMAADcTNjMyFxYV\
FAcDBiMiJyY1NCcTNjMyFxYVFAcDBiMiJyY1NJjNCwoGAwsFzAkLBAYLi80KCwYDCwXMCAwEBgsj\
AcASAgUKBgz+PxEDBgwHBwHAEgIFCgYM/j8RAwYMBwABAAD/BgCCAPoAAwAANTMRI4KC+v4MAAAB\

AAAAAACCAPoAAwAANTMVI4KC+voAAAABAAD/gwEsAAAAAwAAMSEVIQEs/tR9AAABAAAAAAEsAH0A\
AwAANSEVIQEs/tR9fQABAAD+fgDrAYcAEwAAExcHFyYjIgYVFBcmNTQ2MzIXJzcpvWdsMjQfJjh4\
NCUiIodkAYfl2c8uJB01NEtNIy0VvLQAAAEAAP8NAQAAwAAWAAA3DgIjIiY1NDYyFhUUBzI2NzYy\
FwMnqwMZGhMrNyY4KRciMyECFQOWMDwBBwQpKB8gHhkdGyEsAgL+bxAAAAABAAD+DAFIAMAAJAAA\
FwYjIiY1NDYzMhYVFAcyPwEGIyImNTQ2MzIWFRQHMjc2MhcDJ6soISs3JxscKRdBCzw2GCs3Jxsc\
KRdILgIVA8UtxAwoKCAgHxkdGyLKDCkoHyAeGR0bTQIC/W4MAAABAAD+DAGPAcAANgAANwYjIiY1\
NDYzMhYVFAcyPwEiDgEjIiY1NDYzMhYVFAcyNzYyFwEnEwYjIiY1NDYzMhYVFAcyN/YoHys3Jxsc\
KRc/CzoBIBwTKzcnGxwpF0guARYD/vQtVSghKzcnGxwpF0ELPAwoKCAgHxkdGyLLCQQpKB8gHhkd\
G00CAvxuDAEkDCgoICAfGR0bIgAAAAABAAD9DAHaAcAARQAAEwYjIiY1NDYzMhYVFAcyPwEGIyIm\
NTQ2MzIWFRQHMj8BBiMiJjU0NjMyFhUUBzI/ASIOASMiJjU0NjIWFRQHMjc2MhcBJ6soISs3Jxsc\
KRdBCzooISs3JxscKRdBCzooHys3JxscKRc/CzoBIBwTKzcmOCkXSC4CFQP+qS3+PAwoKCAgHxkd\
GyLKDCgoICAfGR0bIsoMKCggIB8ZHRsiywkEKSgfIB4ZHRtNAgL7bgwAAAABAAD9DAIZAq4AVgAA\

JQYjIiY1NDYzMhYVFAcyPwEGIyImNTQ2MzIWFRQHMj8BIg4BIyImNTQ2MzIWFRQHMjc2MhcBJxMG\


IyImNTQ2MzIWFRQHMj8BBiMiJjU0NjMyFhUUBzI3AT8oISs3JxscKRdBCzYoHys3JxscKRc/CzQB\
IBwTKzcnGxwpF0guARYD/motVSghKzcnGxwpF0ELOighKzcnGxwpF0ELNAwoKCAgHxkdGyLEDCgo\

ICAfGR0bIscJBCkoHyAeGR0bTQIC+oAMASQMKCggIB8ZHRsiyAwoKCAgHxkdGyIAA//w/wYCJgD6\


AAcADwATAAA2IiY0NjIWFAAiJjQ2MhYUBQEzAVAyIyMyIwGIMiMjMiP90gG4fv5HSyMyIyMy/s8j\
MiMjMloB9P4MAAT/4f8GAwcA+gAHAA8AEwAXAAA2IiY0NjIWFAAiJjQ2MhYUBQEzATMBMwFBMiMj\
MiMCdzIjIzIj/OMBuHv+R3kBuHv+R0sjMiMjMv7PIzIjIzJaAfT+DAH0/gwAAv+0/4gBfAEYABEA\
OwAANxYzMjY3NjU0JyYjIgYHBhUUFyInBzMyFCsBIjQ7ARM2NTQjIg4DBwYmNzY3NjMyFhc+ATMy\
FhUUBsUCBRIyDg0PAgQSNQsPJyoZLTQLC+ELC0toBgsIDA8LGQoFGwUxDxclIyQHHSYjHi1ZKAE1\
JCAmJQUBMxwnJCkuIHoeHgEdEgwPBxQSLBAIDwlYEBkTGh4PNDBJawAAAf/b//YBvgEYAFMAADcG\
BwYrASI/ATYmIyIGBwYmNz4DMzIXNjMyFz4BMzIWFRQPAQYVFDMyNz4FNzYWBw4CIyImNTQ/ATY1\


NCMiDwEGBycGJj8BNjU0IyIHUAcIBAQ1DQ1CBAYIDRYkBRUEFBAiHxI3CyQkLQkLKRMZJAUvBAgB\


BAULBwwDDQEGFQYSFy0fFRkFNAEVGwhBCA8mDQgEQwEVGwgVEQMBGqcNDxo5CAwJJRowEigoKBAY\
IxoLD30LCQ4CAwkGDwUSAQkNCx8fGhYTDQ2IAgQOFKsWAQEBDwirAgQOFAAAAAH/fv9gAV4BuABB\
AAAHIiY1NDYzMhYVFAcGFRQzMj4HNyMiNTQ7AT4BMzIWFRQGIyImNTQ3NjQjIg4HBzMyFCsBDgEy\
IDAXExIXEgoZCxAPCw0KDg0UCjUTEUEUaTQgMBcTEhcSChkHDAoHCAUGAwYBNhMTPyF2oCYgGiIU\


Dw4LBw0OBhETJSQ8N1UnFRNLXyYgGiIUDw4LBhwFCwoUDRoNHgYoxcEAAAH/2wAAARMBEwAqAAA3\
PgEmIyIGDwEGBwYrASI/ATYmIyIGBwYmNz4BMzIWFzYzMhYVFCMiJjU00AUDAwUUJAk/BwgEBDUN\
DUIEBggOFyIFFQQhOCIbHQQfJBogKw8b5wMHBCwYnBEDARqjDQ8aNQgMCTxAGg0nHhk3Fg0UAAAA\
AQAAAAAA3AEYADEAADMiJjU0NjIWFRQHFjMyNjU0LgInJjU0NjMyFhUUBiMiJjU0NyYjIgYVFB4C\
FxYVFFAeMhUaFxAMExYhCQsYBjo3LSI2FhANFgcPEQ8ZERIeBTArGxAWEAwSEBAWEgsPBw0EJTAj\
KCQYEBgUDgMOExENCRMMEQMgK1UAAAH/5P/8AOcBDwA8AAAnBi4BPwEiBiMiJg4BBwYnJjc+ATce\
ATMyNjMyFxYUDwEGFRQzMjYzMhcWNzYnIjU0NjMyFRQGKwEuASMiCgYLAQWwBR8MAxQMHwUPBAMI\

DAsBDi8VIisECAULB54CAgENBhojDQgFByQSDSYoHh0YJQgOAwQIDQbBBgYCMgUNCwkSHjACAQYL\
AQERB6cGAgMDGgoODQMdDhU3Hi8DGAAF/7T/iAVLARgAEAAiALUAxgDXAAAlFjMyNjc2NCcmIyIG\
BwYVFAUWMzI2NzY1NCcmIyIGBwYVFBciJwczMhQrASI0OwETNjU0IyIOAwcGJjc2NzYzMhYXPgEz\

Mhc+ATMyFhc+ATMyFz4BMzIWFz4BMzIXPgEzMhYXPgEzMhYVFAYjIicHMzIUKwEiNDsBEzY1NCMi\

BgcVFAYjIicHMzIUKwEiNDsBEzY1NCMiBgcVFAYjIicHMzIUKwEiNDsBEzY1NCMiBgcVFAYlFjMy\
Njc2NCcmIyIGBwYVFAUWMzI2NzY0JyYjIgYHBhUUAgoCBRIyDg0PAgQSNQsP/ssCBRIyDg0PAgQS\

NQsPExYZLTQLC+ELC0toBgsIDA8LGQoFGwUxDxclIyQHHSYjLxMSIhsjJAcdJiMvExIiGyMkBx0m\







Iy8TEiIbIyQHHSYjHi1rSRYZLTQLC+ELC0toBgsOFBRrSRYZLTQLC+ELC0toBgsOFBRrSRYZLTQL\
C+ELC0toBgsOFBRrA4MCBRIyDg0PAgQSNQsP/ssCBRIyDg0PAgQSNQsPKAE1JCFKBQEzHCckKQYB\
NSQgJiUFATMcJyQpLiB6Hh4BHRIMDwcUEiwQCA8JWBAZExoeDzIcFhMaHg8yHBYTGh4PMhwWExoe\
DzQwR20geh4eAR0SDA8YIwFHbSB6Hh4BHRIMDxgjAUdtIHoeHgEdEgwPGCMBR20oATUkIUoFATMc\
JyQpBgE1JCFKBQEzHCckKQAE/7T/iAQGARgAbwCAAJEAowAAISInBzMyFCsBIjQ7ARM2NTQjIgYH\
FRQGIyInBzMyFCsBIjQ7ARM2NTQjIgYHFRQGIyInBzMyFCsBIjQ7ARM2NTQjIg4DBwYmNzY3NjMy\

Fhc+ATMyFz4BMzIWFz4BMzIXPgEzMhYXPgEzMhYVFAYnFjMyNjc2NCcmIyIGBwYVFAUWMzI2NzY0\
JyYjIgYHBhUUBRYzMjY3NjU0JyYjIgYHBhUUA1IWGS00CwvhCwtLaAYLDhQUa0kWGS00CwvhCwtL\
aAYLDhQUa0kWGS00CwvhCwtLaAYLCAwPCxkKBRsFMQ8XJSMkBx0mIy8TEiIbIyQHHSYjLxMSIhsj\
JAcdJiMeLWtMAgUSMg4NDwIEEjULD/7LAgUSMg4NDwIEEjULD/7LAgUSMg4NDwIEEjULDyB6Hh4B\
HRIMDxgjAUdtIHoeHgEdEgwPGCMBR20geh4eAR0SDA8HFBIsEAgPCVgQGRMaHg8yHBYTGh4PMhwW\
ExoeDzQwR20oATUkIUoFATMcJyQpBgE1JCFKBQEzHCckKQYBNSQgJiUFATMcJyQpAAAD/7T/iALB\
ARgATABeAG8AADMiJwczMhQrASI0OwETNjU0IyIOAwcGJjc2NzYzMhYXPgEzMhc+ATMyFhc+ATMy\

FhUUBiMiJwczMhQrASI0OwETNjU0IyIGBxUUBicWMzI2NzY1NCcmIyIGBwYVFAUWMzI2NzY0JyYj\
IgYHBhUUyBYZLTQLC+ELC0toBgsIDA8LGQoFGwUxDxclIyQHHSYjLxMSIhsjJAcdJiMeLWtJFhkt\
NAsL4QsLS2gGCw4UFGtMAgUSMg4NDwIEEjULDwFVAgUSMg4NDwIEEjULDyB6Hh4BHRIMDwcUEiwQ\
CA8JWBAZExoeDzIcFhMaHg80MEdtIHoeHgEdEgwPGCMBR20oATUkICYlBQEzHCckKQYBNSQhSgUB\
MxwnJCkAAAAC/9v/iAMrARgAcgCDAAAlNjc2MzIWFz4BMzIWFRQGIyInBzMyFCsBIjQ7ARM2NTQj\
Ig4DBw4BIyImNTQ/ATY1NCMiDwEGBycGJj8BNjU0IyIPAQYHBisBIj8BNiYjIgYHBiY3PgMzMhc2\
MzIXPgEzMhYVFA8BBhUUMzI3PgEXFjMyNjc2NCcmIyIGBwYVFAGtMRQYIiMkBx0mIx4ta0kWGS00\
CwvhCwtLaAYLBw8SDhgIGjwtFRkFNAEVGwhBCA8mDQgEQwEVGwhEBwgEBDUNDUIEBggNFiQFFQQU\
ECIfEjcLJCQtCQspExkkBS8ECAEEDyXRAgUSMg4NDwIEEjULD3dsGB0TGh4PNDBHbSB6Hh4BHRIM\

Dw0fGzUQNjgWEw0NiAIEDhSrFgEBAQ8IqwIEDhSsEQMBGqcNDxo5CAwJJRowEigoKBAYIxoLD30L\
CQ4CCDU5ATUkIUoFATMcJyQpAAAAAAL/2/9gAxkBuABTAJUAADcGBwYrASI/ATYmIyIGBwYmNz4D\
MzIXNjMyFz4BMzIWFRQPAQYVFDMyNz4FNzYWBw4CIyImNTQ/ATY1NCMiDwEGBycGJj8BNjU0IyIH\
EyImNTQ2MzIWFRQHBhUUMzI+BzcjIjU0OwE+ATMyFhUUBiMiJjU0NzY0IyIOBwczMhQrAQ4BUAcI\
BAQ1DQ1CBAYIDRYkBRUEFBAiHxI3CyQkLQkLKRMZJAUvBAgBBAULBwwDDQEGFQYSFy0fFRkFNAEV\
GwhBCA8mDQgEQwEVGwj1IDAXExIXEgoZCxAPCw0KDg0UCjUTEUEUaTQgMBcTEhcSChkHDAoHCAUG\
AwYBNhQUPyF2FREDARqnDQ8aOQgMCSUaMBIoKCgQGCMaCw99CwkOAgMJBg8FEgEJDQsfHxoWEw0N\

iAIEDhSrFgEBAQ8IqwIEDhT+nyYgGiIUDw4LBw0OBhETJSQ8N1UnFRNLXyYgGiIUDw4LBhwFCwoU\
DRoNHgYoxcEAAAAB/37/YAJpAbgAdAAAJSMOASMiJjU0NjMyFhUUBwYVFDMyPgc3IyI1NDsBPgEz\
MhYVFAYjIiY1NDc2NCMiBwYHFz4BMzIWFRQGIyImNTQ3NjQjIg4HBzMyFCsBDgEjIiY1NDYzMhYV\
FAcGFRQzMj4HAV+bIXZfIDAXExIXEgoZCxAPCw0KDg0UCjUTEUEUaTQgMBcTEhcSChkmFwMBmxRp\
NCAwFxMSFxIKGQcMCgcIBQYDBgE2FBQ/IXZfIDAXExIXEgoZCxAPCw0KDw0U5sXBJiAaIhQPDgsH\
DQ4GERMlJDw3VScVE0tfJiAaIhQPDgsGHHMMBgFLXyYgGiIUDw4LBhwFCwoUDRoNHgYoxcEmIBoi\
FA8OCwcNDgYREyUkPDdVAAAAAf9+/2ADdAG4AKoAABMzPgEzMhYVFAYjIiY1NDc2NCMiBwYHFz4B\
MzIWFRQGIyImNTQ3NjQjIg4HBzMyFCsBDgEjIiY1NDYzMhYVFAcGFRQzMj4HNyMOASMiJjU0NjMy\
FhUUBwYVFDMyPgc3Iw4BIyImNTQ2MzIWFRQHBhUUMzI+BzcjIjU0OwE+ATMyFhUUBiMiJjU0NzY0\
IyIOAgcGzJwUaTQgMBcTEhcSChkmFwMBmxRpNCAwFxMSFxIKGQcMCgcIBQYDBgE2ExM/IXZfIDAX\
ExIXEgoZCxAPCw0KDw0UCpshdl8gMBcTEhcSChkLEA8LDQoODRUKmyF2XyAwFxMSFxIKGQsQDwsN\
Cg4NFAo1ExFBFGk0IDAXExIXEgoZDhUOCAYCAQ5LXyYgGiIUDw4LBhxzDAYBS18mIBoiFA8OCwYc\
BQsKFA0aDR4GKMXBJiAaIhQPDgsHDQ4GERMlJDw3VSfFwSYgGiIUDw4LBw0OBhETJSQ7OFUnxcEm\
IBoiFA8OCwcNDgYREyUkPDdVJxUTS18mIBoiFA8OCwYcEyciGwoAAf9+/2AEgAG4AOAAAAEXPgEz\
MhYVFAYjIiY1NDc2NCMiBwYHFz4BMzIWFRQGIyImNTQ3NjQjIg4HBzMyFCsBDgEjIiY1NDYzMhYV\
FAcGFRQzMj4HNyMOASMiJjU0NjMyFhUUBwYVFDMyPgc3Iw4BIyImNTQ2MzIWFRQHBhUUMzI+Bzcj\

DgEjIiY1NDYzMhYVFAcGFRQzMj4HNyMiNTQ7AT4BMzIWFRQGIyImNTQ3NjQjIgcGBxc+ATMyFhUU\
BiMiJjU0NzY0IyIOBwHYnBRpNCAwFxMSFxIKGSYXAwGbFGk0IDAXExIXEgoZBwwKBwgFBgMGATYT\
Ez8hdl8gMBcTEhcSChkLEA8LDQoPDRQKmyF2XyAwFxMSFxIKGQsQDwsNCg4NFAqbIXZfIDAXExIX\
EgoZCxAPCw0KDw0UCpshdl8gMBcTEhcSChkLEA8LDQoODRQKNRMRQRRpNCAwFxMSFxIKGSYXAwGb\
FGk0IDAXExIXEgoZBwwKBwgFBgMGAQ8BS18mIBoiFA8OCwYccwwGAUtfJiAaIhQPDgsGHAULChQN\
Gg0eBijFwSYgGiIUDw4LBw0OBhETJSQ8N1UnxcEmIBoiFA8OCwcNDgYREyUjPDdWJ8XBJiAaIhQP\
DgsHDQ4GERMlJDw3VSfFwSYgGiIUDw4LBw0OBhETJSQ8N1UnFRNLXyYgGiIUDw4LBhxzDAYBS18m\
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\
IRwUVSgUAwcOMCEcFFUoFAQHDfYwET0UAwcMMBE9FAQIAAAAAAIAAP+JAcIAiwALABMAAAU0JiMi\

BhUUFjMyPgEUBiImNDYyAVFaNiA0XTchL3F9yH19yBQ0TycfNEwjdGxLS2xLAAAAAgAA/2wBSAKo\
AA0AHAAAJSYjIgYVFBcWMzI2NTQTMxEUBiMiJyY1NDYzMhcBJA0lPJcGCyY8lwIdfkhPJA9+SEAk\
RBdhKwoJF2ErCQJu/W5DZ0MeHUNnLQAAAAABAAD/eQFAAqgADQAAATMRFAYjIiY1NDYzMhcBIh55\
WTI8elgxHwKo/YBGaTgnRWocAAAAAQAA/3kCCgKoABoAAAERFAYjIiY1NDYzMhcRNTMeBBUUBzY1\
NAFAeVkyPHpYMR8eBi45OCUyEgG5/m9GaTgnRWocAU7vNVxHTnVJSHJBSe8AAAAAAgAA/3kCCwKo\
AB8AKAAAJTURMx4EFRQHFhUUBzY1NCYjERQGIyImNTQ2MzITHgEXNDY1NCYBIh4JMDg2IxUWIgV2\
OHlZMjx6WDE9DoUbAXZr5gFXJEc7QlgzJTIwJC5BGSFfnv7WRmk4J0VqAXY7ojUDDgNfnwAAAQAA\
/84AZAAyAAcAABYiJjQ2MhYURyodHSodMh0qHR0qAAAAAAAOAK4AAQAAAAAAAACDAQgAAQAAAAAA\
AQAHAZwAAQAAAAAAAgAHAbQAAQAAAAAAAwAfAfwAAQAAAAAABAAHAiwAAQAAAAAABQAIAkYAAQAA\
AAAABgAHAl8AAwABBAkAAAEGAAAAAwABBAkAAQAOAYwAAwABBAkAAgAOAaQAAwABBAkAAwA+AbwA\
AwABBAkABAAOAhwAAwABBAkABQAQAjQAAwABBAkABgAOAk8AQwBvAHAAeQByAGkAZwBoAHQAIABc\
ADIANQAxACAAMgAwADEAOAAtADIAMAAyADQAIABKAGUAYQBuAC0ARgByAGEAbgBjAG8AaQBzACAA\
TQBvAGkAbgBlAC4AIABUAGgAaQBzACAAZgBvAG4AdAAgAGkAcwAgAGwAaQBjAGUAbgBzAGUAZAAg\
AHUAbgBkAGUAcgAgAHQAaABlACAAUwBJAEwAIABPAHAAZQBuACAARgBvAG4AdAAgAEwAaQBjAGUA\
bgBzAGUAIABcACgAaAB0AHQAcAA6AC8ALwBzAGMAcgBpAHAAdABzAC4AcwBpAGwALgBvAHIAZwAv\
AE8ARgBMAFwAKQAuAABDb3B5cmlnaHQgXDI1MSAyMDE4LTIwMjQgSmVhbi1GcmFuY29pcyBNb2lu\
ZS4gVGhpcyBmb250IGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBTSUwgT3BlbiBGb250IExpY2Vuc2Ug\
XChodHRwOi8vc2NyaXB0cy5zaWwub3JnL09GTFwpLgAAYQBiAGMAMgBzAHYAZwAAYWJjMnN2ZwAA\
UgBlAGcAdQBsAGEAcgAAUmVndWxhcgAARgBvAG4AdABGAG8AcgBnAGUAIAA6ACAAYQBiAGMAMgBz\
AHYAZwAgADoAIAAzADAALQA0AC0AMgAwADIANAAARm9udEZvcmdlIDogYWJjMnN2ZyA6IDMwLTQt\
MjAyNAAAYQBiAGMAMgBzAHYAZwAAYWJjMnN2ZwAAVgBlAHIAcwBpAG8AbgAgAABWZXJzaW9uIAAA\
YQBiAGMAMgBzAHYAZwAAYWJjMnN2ZwAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AACOAAAAAQACAQIAAwEDAQQBBQEGAQcBCAEJAQoBCwEMAQ0BDgEPARABEQESARMBFAEVARYBFwEY\
ARkBGgEbARwBHQEeAR8BIAEhASIBIwEkASUBJgEnASgBKQEqASsBLAEtAS4BLwEwATEBMgEzATQB\
NQE2ATcBOAE5AToBOwE8AT0BPgE/AUABQQFCAUMBRAFFAUYBRwFIAUkBSgFLAUwBTQFOAU8BUAFR\
AVIBUwFUAVUBVgFXAVgBWQFaAVsBXAFdAV4BXwFgAWEBYgFjAWQBZQFmAWcBaAFpAWoBawFsAW0B\

bgFvAXABcQFyAXMBdAF1AXYBdwF4AXkBegF7AXwBfQF+AX8BgAGBAYIBgwGEAYUBhgGHAYgBiQGK\

AYsGLm5vZGVmB3VuaUUwMDAHdW5pRTAzMAd1bmlFMDM4B3VuaUUwMzkHdW5pRTA0Mwd1bmlFMDQ0\
B3VuaUUwNDUHdW5pRTA0Ngd1bmlFMDQ3B3VuaUUwNDgHdW5pRTA1MAd1bmlFMDVDB3VuaUUwNjIH\
dW5pRTA2OQd1bmlFMDdBB3VuaUUwN0IHdW5pRTA3Qwd1bmlFMDdEB3VuaUUwN0UHdW5pRTA4MAd1\
bmlFMDgxB3VuaUUwODIHdW5pRTA4Mwd1bmlFMDg0B3VuaUUwODUHdW5pRTA4Ngd1bmlFMDg3B3Vu\
aUUwODgHdW5pRTA4OQd1bmlFMDhBB3VuaUUwOEMHdW5pRTA5NAd1bmlFMDk1B3VuaUUwQTAHdW5p\
RTBBMQd1bmlFMEEyB3VuaUUwQTMHdW5pRTBBNAd1bmlFMEE5B3VuaUUwQjMHdW5pRTEwMQd1bmlF\
MUI5B3VuaUUxQkIHdW5pRTFFNwd1bmlFMUYyB3VuaUUxRjQHdW5pRTFGNwd1bmlFMUY5B3VuaUUx\
RkMHdW5pRTFGRQd1bmlFMUZGB3VuaUUyMDAHdW5pRTI0MAd1bmlFMjQxB3VuaUUyNDIHdW5pRTI0\
Mwd1bmlFMjQ0B3VuaUUyNDUHdW5pRTI0Ngd1bmlFMjQ3B3VuaUUyNDgHdW5pRTI0OQd1bmlFMjYw\
B3VuaUUyNjEHdW5pRTI2Mgd1bmlFMjYzB3VuaUUyNjQHdW5pRTI2YQd1bmlFMjZiB3VuaUUyODAH\
dW5pRTI4MQd1bmlFMjgyB3VuaUUyODMHdW5pRTRBMAd1bmlFNEEyB3VuaUU0QTQHdW5pRTRBOAd1\
bmlFNEFDB3VuaUU0QzAHdW5pRTRDRQd1bmlFNEQxB3VuaUU0RTEHdW5pRTRFMgd1bmlFNEUzB3Vu\
aUU0RTQHdW5pRTRFNQd1bmlFNEU2B3VuaUU0RTcHdW5pRTRFOAd1bmlFNEU5B3VuaUU0RUEHdW5p\
RTUwMAd1bmlFNTAxB3VuaUU1MjAHdW5pRTUyMQd1bmlFNTIyB3VuaUU1MjMHdW5pRTUyNAd1bmlF\
NTI1B3VuaUU1MjkHdW5pRTUyQQd1bmlFNTJCB3VuaUU1MkMHdW5pRTUyRAd1bmlFNTJGB3VuaUU1\
MzAHdW5pRTUzMQd1bmlFNTM5B3VuaUU1NjYHdW5pRTU2Nwd1bmlFNTY5B3VuaUU1NkMHdW5pRTU2\
RAd1bmlFNTgyB3VuaUU1RDAHdW5pRTVFMgd1bmlFNjEwB3VuaUU2MTIHdW5pRTYxNAd1bmlFNjE4\
B3VuaUU2MjQHdW5pRTYzMAd1bmlFNjUwB3VuaUU2NTUHdW5pRTkxMQd1bmlFOTE1B3VuaUU5MjAH\
dW5pRTkyNQd1bmlFOTVEB3VuaUVBMDIHdW5pRUFBNAd1bmlFQ0EyB3VuaUVDQTMHdW5pRUNBNQd1\
bmlFQ0E3B3VuaUVDQTkHdW5pRUNCNwAAAAAAAAH//wACAAEAAAAAAAAADAAUAAQAAAACAAAAAQAA\
AAEAAAAAAAEAAAAA4UXjegAAAADRlyIXAAAAAOJWUs0=\
") format("truetype")'
Changes to font/abc2svg.sfd.
︙︙
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Layer: 1 0 "Fore" 0
XUID: [1021 296 1430826605 13506888]
FSType: 0
OS2Version: 0
OS2_WeightWidthSlopeOnly: 0
OS2_UseTypoMetrics: 0
CreationTime: 1433498007
ModificationTime: 1705513399
PfmFamily: 17
TTFWeight: 400
TTFWidth: 5
LineGap: 92
VLineGap: 92
Panose: 2 0 5 3 0 0 0 0 0 0
OS2TypoAscent: 0







|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Layer: 1 0 "Fore" 0
XUID: [1021 296 1430826605 13506888]
FSType: 0
OS2Version: 0
OS2_WeightWidthSlopeOnly: 0
OS2_UseTypoMetrics: 0
CreationTime: 1433498007
ModificationTime: 1714463309
PfmFamily: 17
TTFWeight: 400
TTFWidth: 5
LineGap: 92
VLineGap: 92
Panose: 2 0 5 3 0 0 0 0 0 0
OS2TypoAscent: 0
︙︙
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
LangName: 1033
Encoding: Custom
UnicodeInterp: none
NameList: Adobe Glyph List
DisplaySize: -96
AntiAlias: 1
FitToEm: 1
WinInfo: 32 8 2
BeginPrivate: 0
EndPrivate
TeXData: 1 0 0 346030 173015 115343 0 1048576 115343 783286 444596 497025 792723 393216 433062 380633 303038 157286 324010 404750 52429 2506097 1059062 262144
BeginChars: 143 143

StartChar: .nodef
Encoding: 0 0 0
Width: 400
Flags: M
LayerCount: 2
Fore







|



|







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
LangName: 1033
Encoding: Custom
UnicodeInterp: none
NameList: Adobe Glyph List
DisplaySize: -96
AntiAlias: 1
FitToEm: 1
WinInfo: 104 8 2
BeginPrivate: 0
EndPrivate
TeXData: 1 0 0 346030 173015 115343 0 1048576 115343 783286 444596 497025 792723 393216 433062 380633 303038 157286 324010 404750 52429 2506097 1059062 262144
BeginChars: 139 139

StartChar: .nodef
Encoding: 0 0 0
Width: 400
Flags: M
LayerCount: 2
Fore
︙︙
177
178
179
180
181
182
183
184
185
186















187
188
189
190
191
192
193
 22 320 0 342 0 370 c 7
 0 398 22 420 50 420 c 7
 78 420 100 398 100 370 c 7
 100 342 78 320 50 320 c 7
EndSplineSet
Validated: 1
EndChar

StartChar: uniE045
Encoding: 7 57413 7















Width: 1059
Flags: HMW
LayerCount: 2
Fore
SplineSet
590 0 m 24
 590 160 l 24








|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
 22 320 0 342 0 370 c 7
 0 398 22 420 50 420 c 7
 78 420 100 398 100 370 c 7
 100 342 78 320 50 320 c 7
EndSplineSet
Validated: 1
EndChar

StartChar: uniE044
Encoding: 7 57412 7
Width: 100
Flags: HMW
LayerCount: 2
Fore
SplineSet
50 -50 m 7
 22 -50 0 -28 0 0 c 7
 0 28 22 50 50 50 c 7
 78 50 100 28 100 0 c 7
 100 -28 78 -50 50 -50 c 7
EndSplineSet
EndChar

StartChar: uniE045
Encoding: 8 57413 8
Width: 1059
Flags: HMW
LayerCount: 2
Fore
SplineSet
590 0 m 24
 590 160 l 24
︙︙
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
 470 60 370 0 220 0 c 0
 147 0 73 0 0 0 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE046
Encoding: 8 57414 8
Width: 1061
Flags: HMW
LayerCount: 2
Fore
SplineSet
770 0 m 0
 636 0 550 70 550 210 c 0







|







252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
 470 60 370 0 220 0 c 0
 147 0 73 0 0 0 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE046
Encoding: 9 57414 9
Width: 1061
Flags: HMW
LayerCount: 2
Fore
SplineSet
770 0 m 0
 636 0 550 70 550 210 c 0
︙︙
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
 470 60 370 0 220 0 c 0
 147 0 73 0 0 0 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE047
Encoding: 9 57415 9
Width: 480
Flags: HMW
LayerCount: 2
Fore
SplineSet
398 338 m 3
 376 338 358 356 358 378 c 3







|







303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
 470 60 370 0 220 0 c 0
 147 0 73 0 0 0 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE047
Encoding: 10 57415 10
Width: 480
Flags: HMW
LayerCount: 2
Fore
SplineSet
398 338 m 3
 376 338 358 356 358 378 c 3
︙︙
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
 328 5 299 2 271 2 c 0
 203 2 167 51 167 91 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE048
Encoding: 10 57416 10
Width: 954
Flags: HMW
LayerCount: 2
Fore
SplineSet
452 866 m 1
 504 866 l 1







|







353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
 328 5 299 2 271 2 c 0
 203 2 167 51 167 91 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE048
Encoding: 11 57416 11
Width: 954
Flags: HMW
LayerCount: 2
Fore
SplineSet
452 866 m 1
 504 866 l 1
︙︙
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
 373 629 309 565 304 374 c 1
 452 374 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE050
Encoding: 11 57424 11
Width: 779
Flags: HMW
LayerCount: 2
Fore
SplineSet
364 851 m 0
 359 812 357 782 357 751 c 0







|







397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
 373 629 309 565 304 374 c 1
 452 374 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE050
Encoding: 12 57424 12
Width: 779
Flags: HMW
LayerCount: 2
Fore
SplineSet
364 851 m 0
 359 812 357 782 357 751 c 0
︙︙
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
 387 -222 397 -221 408 -220 c 0
 375 138 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE05C
Encoding: 12 57436 12
Width: 722
Flags: MW
LayerCount: 2
Fore
SplineSet
478 -480 m 0
 567 -480 614 -375 614 -286 c 0







|







453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
 387 -222 397 -221 408 -220 c 0
 375 138 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE05C
Encoding: 13 57436 13
Width: 722
Flags: MW
LayerCount: 2
Fore
SplineSet
478 -480 m 0
 567 -480 614 -375 614 -286 c 0
︙︙
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
 0 -514 l 1
 0 510 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE062
Encoding: 13 57442 13
Width: 703
Flags: HMW
LayerCount: 2
Fore
SplineSet
19 29 m 0
 19 161 125 255 261 255 c 0







|







519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
 0 -514 l 1
 0 510 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE062
Encoding: 14 57442 14
Width: 703
Flags: HMW
LayerCount: 2
Fore
SplineSet
19 29 m 0
 19 161 125 255 261 255 c 0
︙︙
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
 681 174 702 153 702 127 c 0
 702 94 682 72 655 72 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE069
Encoding: 14 57449 14
Width: 470
Flags: W
VStem: 0 150<-250 250> 220 150<-250 250>
LayerCount: 2
Fore
SplineSet
220 250 m 1







|







557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
 681 174 702 153 702 127 c 0
 702 94 682 72 655 72 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE069
Encoding: 15 57449 15
Width: 470
Flags: W
VStem: 0 150<-250 250> 220 150<-250 250>
LayerCount: 2
Fore
SplineSet
220 250 m 1
︙︙
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
 0 -250 l 1
 0 250 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE07A
Encoding: 15 57466 15
Width: 779
Flags: MW
LayerCount: 2
Fore
SplineSet
329 117 m 1
 329 117 348 -79 354 -170 c 1







|







579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
 0 -250 l 1
 0 250 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE07A
Encoding: 16 57466 16
Width: 779
Flags: MW
LayerCount: 2
Fore
SplineSet
329 117 m 1
 329 117 348 -79 354 -170 c 1
︙︙
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
 442 749 433 790 401 820 c 1
 331 792 299 728 290 658 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE07B
Encoding: 16 57467 16
Width: 782
Flags: MW
LayerCount: 2
Fore
SplineSet
382 -384 m 0
 453 -384 491 -300 491 -229 c 0







|







639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
 442 749 433 790 401 820 c 1
 331 792 299 728 290 658 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE07B
Encoding: 17 57467 17
Width: 782
Flags: MW
LayerCount: 2
Fore
SplineSet
382 -384 m 0
 453 -384 491 -300 491 -229 c 0
︙︙
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
 0 -411 l 1
 0 408 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE07C
Encoding: 17 57468 17
Width: 807
Flags: MW
LayerCount: 2
Fore
SplineSet
15 33 m 0
 15 139 104 204 209 204 c 0







|







705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
 0 -411 l 1
 0 408 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE07C
Encoding: 18 57468 18
Width: 807
Flags: MW
LayerCount: 2
Fore
SplineSet
15 33 m 0
 15 139 104 204 209 204 c 0
︙︙
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
 545 139 562 123 562 102 c 0
 562 76 546 58 524 58 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE07D
Encoding: 18 57469 18
Width: 200
Flags: HMW
LayerCount: 2
Fore
SplineSet
86 116 m 1
 58 106 42 90 42 70 c 4







|







743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
 545 139 562 123 562 102 c 0
 562 76 546 58 524 58 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE07D
Encoding: 19 57469 19
Width: 200
Flags: HMW
LayerCount: 2
Fore
SplineSet
86 116 m 1
 58 106 42 90 42 70 c 4
︙︙
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
 169 110 180 90 180 70 c 0
 180 27 140 0 90 0 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE07E
Encoding: 19 57470 19
Width: 360
Flags: HMW
LayerCount: 2
Fore
SplineSet
212 -1 m 0
 171 -1 150 17 150 54 c 0







|







773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
 169 110 180 90 180 70 c 0
 180 27 140 0 90 0 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE07E
Encoding: 20 57470 20
Width: 360
Flags: HMW
LayerCount: 2
Fore
SplineSet
212 -1 m 0
 171 -1 150 17 150 54 c 0
︙︙
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
 79 -0 67 0 52 -0 c 0
 38 0 25 0 9 0 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE080
Encoding: 20 57472 20
Width: 430
Flags: HMW
LayerCount: 2
Fore
SplineSet
138 0 m 1
 138 -130 168 -218 215 -218 c 1
 260 -218 292 -130 292 0 c 1
 292 130 268 218 215 218 c 1
 165 218 138 130 138 0 c 1
0 3 m 1
 0 140 95 250 215 250 c 1
 335 250 430 140 430 0 c 1
 430 -140 335 -250 215 -250 c 1
 95 -250 0 -140 0 3 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE081
Encoding: 21 57473 21
Width: 290
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 0 m 25
 100 250 l 25







|




















|







834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
 79 -0 67 0 52 -0 c 0
 38 0 25 0 9 0 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE080
Encoding: 21 57472 21
Width: 430
Flags: HMW
LayerCount: 2
Fore
SplineSet
138 0 m 1
 138 -130 168 -218 215 -218 c 1
 260 -218 292 -130 292 0 c 1
 292 130 268 218 215 218 c 1
 165 218 138 130 138 0 c 1
0 3 m 1
 0 140 95 250 215 250 c 1
 335 250 430 140 430 0 c 1
 430 -140 335 -250 215 -250 c 1
 95 -250 0 -140 0 3 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE081
Encoding: 22 57473 22
Width: 290
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 0 m 25
 100 250 l 25
︙︙
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
 115 100 l 29
 0 0 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniE082
Encoding: 22 57474 22
Width: 400
Flags: HMW
HStem: -250 500
VStem: 0 400
LayerCount: 2
Fore
SplineSet







|







877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
 115 100 l 29
 0 0 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniE082
Encoding: 23 57474 23
Width: 400
Flags: HMW
HStem: -250 500
VStem: 0 400
LayerCount: 2
Fore
SplineSet
︙︙
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
 28 25 0 60 0 128 c 0
 0 213 102 250 199 250 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE083
Encoding: 23 57475 23
Width: 380
Flags: HMW
HStem: -250 500
LayerCount: 2
Fore
SplineSet
102 170 m 1







|







912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
 28 25 0 60 0 128 c 0
 0 213 102 250 199 250 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE083
Encoding: 24 57475 24
Width: 380
Flags: HMW
HStem: -250 500
LayerCount: 2
Fore
SplineSet
102 170 m 1
︙︙
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
 252 177 223 213 175 213 c 0
 131 213 109 193 102 170 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE084
Encoding: 24 57476 24
Width: 400
Flags: HMW
HStem: -250 500
VStem: 0 400
LayerCount: 2
Fore
SplineSet







|







949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
 252 177 223 213 175 213 c 0
 131 213 109 193 102 170 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE084
Encoding: 25 57476 25
Width: 400
Flags: HMW
HStem: -250 500
VStem: 0 400
LayerCount: 2
Fore
SplineSet
︙︙
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
 325 -160 l 5
 325 -209 l 5
EndSplineSet
Validated: 1
EndChar

StartChar: uniE085
Encoding: 25 57477 25
Width: 400
Flags: HMW
HStem: -250 500
LayerCount: 2
Fore
SplineSet
18 -95 m 0







|







980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
 325 -160 l 5
 325 -209 l 5
EndSplineSet
Validated: 1
EndChar

StartChar: uniE085
Encoding: 26 57477 26
Width: 400
Flags: HMW
HStem: -250 500
LayerCount: 2
Fore
SplineSet
18 -95 m 0
︙︙
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
 25 -217 0 -176 0 -152 c 0
 0 -135 3 -113 18 -95 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE086
Encoding: 26 57478 26
Width: 385
Flags: HMW
HStem: -250 500
LayerCount: 2
Fore
SplineSet
200 -210 m 0







|







1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
 25 -217 0 -176 0 -152 c 0
 0 -135 3 -113 18 -95 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE086
Encoding: 27 57478 27
Width: 385
Flags: HMW
HStem: -250 500
LayerCount: 2
Fore
SplineSet
200 -210 m 0
︙︙
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
 1 133 74 250 200 250 c 0
 257 250 298 244 340 205 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE087
Encoding: 27 57479 27
Width: 400
Flags: HMW
HStem: -250 500
LayerCount: 2
Fore
SplineSet
97 161 m 0







|







1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
 1 133 74 250 200 250 c 0
 257 250 298 244 340 205 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE087
Encoding: 28 57479 28
Width: 400
Flags: HMW
HStem: -250 500
LayerCount: 2
Fore
SplineSet
97 161 m 0
︙︙
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
 305 125 280 121 268 121 c 0
 194 121 173 161 97 161 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE088
Encoding: 28 57480 28
Width: 400
Flags: HMW
HStem: -250 500
LayerCount: 2
Fore
SplineSet
154 -42 m 1







|







1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
 305 125 280 121 268 121 c 0
 194 121 173 161 97 161 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE088
Encoding: 29 57480 29
Width: 400
Flags: HMW
HStem: -250 500
LayerCount: 2
Fore
SplineSet
154 -42 m 1
︙︙
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
 93 -250 0 -194 0 -130 c 0
 0 -77 50 -44 108 -16 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE089
Encoding: 29 57481 29
Width: 385
Flags: HMW
HStem: -250 500
LayerCount: 2
Fore
SplineSet
185 210 m 0







|







1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
 93 -250 0 -194 0 -130 c 0
 0 -77 50 -44 108 -16 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE089
Encoding: 30 57481 30
Width: 385
Flags: HMW
HStem: -250 500
LayerCount: 2
Fore
SplineSet
185 210 m 0
︙︙
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
 384 -133 311 -250 185 -250 c 0
 128 -250 87 -244 45 -205 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE08A
Encoding: 30 57482 30
Width: 409
Flags: HMW
LayerCount: 2
Fore
SplineSet
303 164 m 1
 311 163 315 162 321 161 c 1







|







1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
 384 -133 311 -250 185 -250 c 0
 128 -250 87 -244 45 -205 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE08A
Encoding: 31 57482 31
Width: 409
Flags: HMW
LayerCount: 2
Fore
SplineSet
303 164 m 1
 311 163 315 162 321 161 c 1
︙︙
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
 280 249 325 222 345 199 c 0
 365 176 379 150 379 124 c 0
 379 77 354 30 315 30 c 0
 270 30 242 69 242 104 c 1
 244 130 265 164 302 164 c 2
 303 164 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE08B
Encoding: 31 57483 31
Width: 408
Flags: MW
LayerCount: 2
Fore
SplineSet
303 164 m 1
 311 163 315 162 321 161 c 1
 324 165 325 170 325 174 c 0
 325 202 273 228 234 228 c 0
 226 228 223 226 215 224 c 1
 215 -213 l 1
 224 -216 229 -217 239 -217 c 0
 265 -217 294 -207 322 -183 c 0
 350 -159 369 -119 392 -71 c 1
 392 -74 410 -77 409 -80 c 0
 377 -180 338 -244 216 -246 c 1
 215 -246 l 1
 215 -350 l 1
 180 -350 l 1
 180 -243 l 1
 141 -237 103 -218 73 -189 c 0
 34 -151 13 -98 10 -30 c 0
 10 -26 9 13 9 17 c 0
 9 164 76 230 180 245 c 1
 180 350 l 1
 215 350 l 1
 215 248 l 1
 221 248 222 249 228 249 c 0
 280 249 325 222 345 199 c 0
 365 176 379 150 379 124 c 0
 379 77 354 30 315 30 c 0
 270 30 242 69 242 104 c 1
 244 130 265 164 302 164 c 2
 303 164 l 1
180 -197 m 1
 180 209 l 1
 144 182 119 122 119 18 c 0
 119 -58 126 -133 158 -175 c 0
 165 -184 172 -191 180 -197 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE08C
Encoding: 32 57484 32
Width: 500
Flags: HMW







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







1157
1158
1159
1160
1161
1162
1163













































1164
1165
1166
1167
1168
1169
1170
 280 249 325 222 345 199 c 0
 365 176 379 150 379 124 c 0
 379 77 354 30 315 30 c 0
 270 30 242 69 242 104 c 1
 244 130 265 164 302 164 c 2
 303 164 l 1
EndSplineSet













































Validated: 1
EndChar

StartChar: uniE08C
Encoding: 32 57484 32
Width: 500
Flags: HMW
︙︙
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
StartChar: uniE5D0
Encoding: 116 58832 116
Width: 310
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 0 m 1
 0 60 l 1
 93 79 229 114 280 370 c 1
 310 370 l 1
 310 175 152 13 0 0 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE5E2
Encoding: 117 58850 117
Width: 300
Flags: W
HStem: 0 62<198.921 262.211> 96 64<38.8054 112.592>







|
|
|
|
|

<







3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976

3977
3978
3979
3980
3981
3982
3983
StartChar: uniE5D0
Encoding: 116 58832 116
Width: 310
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 -370 m 5
 0 -310 l 5
 93 -291 229 -256 280 0 c 5
 310 0 l 5
 310 -195 152 -357 0 -370 c 5
EndSplineSet

EndChar

StartChar: uniE5E2
Encoding: 117 58850 117
Width: 300
Flags: W
HStem: 0 62<198.921 262.211> 96 64<38.8054 112.592>
︙︙
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
 135 161 150 163 166 179 c 0
 173 186 179 196 179 204 c 0
 179 219 166 230 141 230 c 0xcb40
EndSplineSet
Validated: 1
EndChar

StartChar: uniE910
Encoding: 126 59664 126
Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
45 0 m 28
 45 -113 137 -205 250 -205 c 28
 363 -205 455 -113 455 0 c 28
 455 113 363 205 250 205 c 28
 137 205 45 113 45 0 c 28
0 0 m 28
 0 139 111 250 250 250 c 28
 388 250 500 139 500 0 c 28
 500 -138 389 -250 250 -250 c 28
 112 -250 0 -138 0 0 c 28
250 -60 m 7
 217 -60 190 -33 190 0 c 7
 190 33 217 60 250 60 c 7
 283 60 310 33 310 0 c 7
 310 -33 283 -60 250 -60 c 7
EndSplineSet
Validated: 1
EndChar

StartChar: uniE911
Encoding: 127 59665 127
Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
45 0 m 24
 45 -113 137 -205 250 -205 c 24
 363 -205 455 -113 455 0 c 24
 455 113 363 205 250 205 c 24
 137 205 45 113 45 0 c 24
0 0 m 24
 0 139 111 250 250 250 c 24
 388 250 500 139 500 0 c 24
 500 -138 389 -250 250 -250 c 24
 112 -250 0 -138 0 0 c 24
EndSplineSet
Validated: 1
EndChar

StartChar: uniE912
Encoding: 128 59666 128
Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
228 350 m 5
 273 350 l 5
 273 250 l 5
 400 237 500 131 500 0 c 4
 500 -130 401 -237 273 -250 c 5
 273 -350 l 5
 228 -350 l 5
 228 -249 l 5
 100 -238 0 -131 0 0 c 4
 0 132 99 238 228 249 c 5
 228 350 l 5
228 -204 m 5
 228 204 l 5
 125 193 45 106 45 0 c 4
 45 -106 125 -193 228 -204 c 5
273 -204 m 5
 375 -193 455 -105 455 0 c 4
 455 105 375 193 273 204 c 5
 273 -204 l 5
EndSplineSet
Validated: 1
EndChar

StartChar: uniE914
Encoding: 129 59668 129
Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
463 130 m 0
 464 127 465 123 465 120 c 0
 465 112 461 103 454 99 c 0
 451 97 447 96 443 96 c 0
 435 96 427 100 420 110 c 1
 386 173 323 205 250 205 c 8
 137 205 45 113 45 0 c 24
 45 -113 137 -205 250 -205 c 16
 330 -205 386 -174 420 -110 c 0
 424 -102 434 -97 444 -97 c 0
 448 -97 451 -97 454 -99 c 0
 462 -104 466 -111 466 -118 c 0
 466 -122 465 -126 463 -130 c 0
 420 -211 350 -250 250 -250 c 8
 112 -250 0 -138 0 0 c 24
 0 139 111 250 250 250 c 0
 344 250 420 210 463 130 c 0
250 -60 m 3
 217 -60 190 -33 190 0 c 3
 190 33 217 60 250 60 c 3
 283 60 310 33 310 0 c 3
 310 -33 283 -60 250 -60 c 3
EndSplineSet
Validated: 1
EndChar

StartChar: uniE915
Encoding: 130 59669 130
Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
463 130 m 0
 464 127 465 123 465 120 c 0







|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<



















|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







4281
4282
4283
4284
4285
4286
4287
4288
4289


























4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310































































4311
4312
4313
4314
4315
4316
4317
 135 161 150 163 166 179 c 0
 173 186 179 196 179 204 c 0
 179 219 166 230 141 230 c 0xcb40
EndSplineSet
Validated: 1
EndChar

StartChar: uniE911
Encoding: 126 59665 126


























Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
45 0 m 24
 45 -113 137 -205 250 -205 c 24
 363 -205 455 -113 455 0 c 24
 455 113 363 205 250 205 c 24
 137 205 45 113 45 0 c 24
0 0 m 24
 0 139 111 250 250 250 c 24
 388 250 500 139 500 0 c 24
 500 -138 389 -250 250 -250 c 24
 112 -250 0 -138 0 0 c 24
EndSplineSet
Validated: 1
EndChar

StartChar: uniE915
Encoding: 127 59669 127































































Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
463 130 m 0
 464 127 465 123 465 120 c 0
︙︙
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
 112 -250 0 -138 0 0 c 24
 0 139 111 250 250 250 c 0
 344 250 420 210 463 130 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE918
Encoding: 131 59672 131
Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
273 249 m 1
 356 243 424 203 463 130 c 0
 464 127 465 123 465 120 c 0
 465 112 461 103 454 99 c 0
 451 97 447 96 443 96 c 0
 435 96 427 100 420 110 c 0
 390 166 336 198 273 204 c 1
 273 -204 l 1
 341 -199 389 -168 420 -110 c 0
 424 -102 434 -97 444 -97 c 0
 448 -97 451 -97 454 -99 c 0
 462 -104 466 -111 466 -118 c 0
 466 -122 465 -126 463 -130 c 0
 423 -205 361 -244 273 -249 c 1
 273 -350 l 1
 228 -350 l 1
 228 -249 l 1
 100 -238 0 -131 0 0 c 0
 0 132 99 238 228 249 c 1
 228 350 l 1
 273 350 l 1
 273 249 l 1
228 -204 m 1
 228 204 l 1
 125 193 45 106 45 0 c 0
 45 -106 125 -193 228 -204 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE920
Encoding: 132 59680 132
Width: 120
Flags: HMW
LayerCount: 2
Fore
SplineSet
60 -60 m 3
 27 -60 0 -33 0 0 c 3
 0 33 27 60 60 60 c 3
 93 60 120 33 120 0 c 3
 120 -33 93 -60 60 -60 c 3
EndSplineSet
Validated: 1
EndChar

StartChar: uniE925
Encoding: 133 59685 133
Width: 45
Flags: HMW
LayerCount: 2
Fore
SplineSet
45 350 m 9
 45 -350 l 1
 0 -350 l 9
 0 350 l 1
 45 350 l 9
EndSplineSet
Validated: 1
EndChar

StartChar: uniE95D
Encoding: 134 59741 134
Width: 540
Flags: HMW
LayerCount: 2
Fore
SplineSet
30 70 m 25
 30 -70 l 25







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

|





|
|
|
|
|





|















|







4330
4331
4332
4333
4334
4335
4336





































4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
 112 -250 0 -138 0 0 c 24
 0 139 111 250 250 250 c 0
 344 250 420 210 463 130 c 0
EndSplineSet
Validated: 1
EndChar






































StartChar: uniE920
Encoding: 128 59680 128
Width: 120
Flags: HMW
LayerCount: 2
Fore
SplineSet
60 -60 m 7
 27 -60 0 -33 0 0 c 7
 0 33 27 60 60 60 c 7
 93 60 120 33 120 0 c 7
 120 -33 93 -60 60 -60 c 7
EndSplineSet
Validated: 1
EndChar

StartChar: uniE925
Encoding: 129 59685 129
Width: 45
Flags: HMW
LayerCount: 2
Fore
SplineSet
45 350 m 9
 45 -350 l 1
 0 -350 l 9
 0 350 l 1
 45 350 l 9
EndSplineSet
Validated: 1
EndChar

StartChar: uniE95D
Encoding: 130 59741 130
Width: 540
Flags: HMW
LayerCount: 2
Fore
SplineSet
30 70 m 25
 30 -70 l 25
︙︙
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
 0 -190 l 25
 0 190 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniEA02
Encoding: 135 59906 135
Width: 250
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 65 m 25
 137 250 l 25







|







4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
 0 -190 l 25
 0 190 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniEA02
Encoding: 131 59906 131
Width: 250
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 65 m 25
 137 250 l 25
︙︙
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
 32 41 l 25
 0 65 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniEAA4
Encoding: 136 60068 136
Width: 250
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 270 m 0
 30 300 45 311 85 311 c 0







|







4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
 32 41 l 25
 0 65 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniEAA4
Encoding: 132 60068 132
Width: 250
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 270 m 0
 30 300 45 311 85 311 c 0
︙︙
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
 -17 226 -24 227 -24 234 c 0
 -24 241 -17 253 0 270 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniECA2
Encoding: 137 60578 137
Width: 450
Flags: HMW
LayerCount: 2
Fore
SplineSet
337 -20 m 4
 337 50 264 111 193 111 c 4
 154 111 109 85 109 41 c 4
 109 -30 186 -87 257 -87 c 4
 298 -87 337 -65 337 -20 c 4
450 10 m 4
 450 -60 361 -119 225 -119 c 4
 89 -119 0 -60 0 10 c 4
 0 80 89 139 225 139 c 4
 361 139 450 80 450 10 c 4
EndSplineSet
Validated: 1
EndChar

StartChar: uniECA3
Encoding: 138 60579 138
Width: 328
Flags: HMW
LayerCount: 2
Fore
SplineSet
292 68 m 0
 283 84 264 91 242 91 c 0







|




















|







4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
 -17 226 -24 227 -24 234 c 0
 -24 241 -17 253 0 270 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniECA2
Encoding: 133 60578 133
Width: 450
Flags: HMW
LayerCount: 2
Fore
SplineSet
337 -20 m 4
 337 50 264 111 193 111 c 4
 154 111 109 85 109 41 c 4
 109 -30 186 -87 257 -87 c 4
 298 -87 337 -65 337 -20 c 4
450 10 m 4
 450 -60 361 -119 225 -119 c 4
 89 -119 0 -60 0 10 c 4
 0 80 89 139 225 139 c 4
 361 139 450 80 450 10 c 4
EndSplineSet
Validated: 1
EndChar

StartChar: uniECA3
Encoding: 134 60579 134
Width: 328
Flags: HMW
LayerCount: 2
Fore
SplineSet
292 68 m 0
 283 84 264 91 242 91 c 0
︙︙
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
 237 148 273 134 298 103 c 1
 299 680 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniECA5
Encoding: 139 60581 139
Width: 320
Flags: HMW
LayerCount: 2
Fore
SplineSet
290 680 m 1
 320 680 l 1
 320 40 l 2
 320 -51 231 -135 110 -135 c 0
 40 -135 0 -90 0 -40 c 0
 0 50 90 135 210 135 c 0
 244 135 271 124 290 107 c 1
 290 680 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniECA7
Encoding: 140 60583 140
Width: 522
Flags: HMW
LayerCount: 2
Fore
SplineSet
320 441 m 1
 320 40 l 2







|


















|







4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
 237 148 273 134 298 103 c 1
 299 680 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniECA5
Encoding: 135 60581 135
Width: 320
Flags: HMW
LayerCount: 2
Fore
SplineSet
290 680 m 1
 320 680 l 1
 320 40 l 2
 320 -51 231 -135 110 -135 c 0
 40 -135 0 -90 0 -40 c 0
 0 50 90 135 210 135 c 0
 244 135 271 124 290 107 c 1
 290 680 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniECA7
Encoding: 136 60583 136
Width: 522
Flags: HMW
LayerCount: 2
Fore
SplineSet
320 441 m 1
 320 40 l 2
︙︙
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
 485 57 490 104 490 148 c 0
 490 258 462 396 320 441 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniECA9
Encoding: 141 60585 141
Width: 522
Flags: HMW
LayerCount: 2
Fore
SplineSet
290 107 m 1
 290 337 l 1







|







4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
 485 57 490 104 490 148 c 0
 490 258 462 396 320 441 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniECA9
Encoding: 137 60585 137
Width: 522
Flags: HMW
LayerCount: 2
Fore
SplineSet
290 107 m 1
 290 337 l 1
︙︙
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
 494 242 495 248 495 255 c 0
 495 388 391 509 320 509 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniECB7
Encoding: 142 60599 142
Width: 100
Flags: HMW
LayerCount: 2
Fore
SplineSet
50 -50 m 3
 22 -50 0 -28 0 0 c 3







|







4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
 494 242 495 248 495 255 c 0
 495 388 391 509 320 509 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniECB7
Encoding: 138 60599 138
Width: 100
Flags: HMW
LayerCount: 2
Fore
SplineSet
50 -50 m 3
 22 -50 0 -28 0 0 c 3
︙︙
Changes to modules/MIDI.js.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
// MIDI.js - module to handle the %%MIDI parameters
//
// Copyright (C) 2019-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 Lesser 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.
//
// This module is loaded when "%%MIDI" appears in a ABC source.
//
// Parameters (see abcMIDI for details)
//	%%MIDI channel n
//	%%MIDI program [channel] n
//	%%MIDI control k v
//	%%MIDI drummap ABC_note MIDI_pitch
//	%%MIDI temperamentequal nedo
//	%%MIDI chordname <chord_type> <list of MIDI pitches>
//	%%MIDI chordprog <#MIDI program> [octave=<n>]
//	%%MIDI chordvol <volume>

//	%%MIDI gchordon
//	%%MIDI gchordoff

// Using %%MIDI drummap creates a voicemap named "MIDIdrum".
// This name must be used if some print map is required:
//	%%MIDI drummap g 42
//	%%map MIDIdrum g heads=x


|


















|








>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// MIDI.js - module to handle the %%MIDI parameters
//
// Copyright (C) 2019-2024 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 Lesser 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.
//
// This module is loaded when "%%MIDI" appears in a ABC source.
//
// Parameters (see abcMIDI - https://abcmidi.sourceforge.io/ - for details)
//	%%MIDI channel n
//	%%MIDI program [channel] n
//	%%MIDI control k v
//	%%MIDI drummap ABC_note MIDI_pitch
//	%%MIDI temperamentequal nedo
//	%%MIDI chordname <chord_type> <list of MIDI pitches>
//	%%MIDI chordprog <#MIDI program> [octave=<n>]
//	%%MIDI chordvol <volume>
//	%%MIDI gchord <string>
//	%%MIDI gchordon
//	%%MIDI gchordoff

// Using %%MIDI drummap creates a voicemap named "MIDIdrum".
// This name must be used if some print map is required:
//	%%MIDI drummap g 42
//	%%map MIDIdrum g heads=x
︙︙
117
118
119
120
121
122
123
124
125
126
127
128



129
130
131
132
133
134
135
136
137
138
139
140
141
142

143
144


145


146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//	case "drone":		// %%MIDI drone <#prog> <pit_1> <pit_2> <vol_1> <vol_2>
//				//	default: 70 45 33 80 80
//		break
//	case "droneon":		// %%MIDI droneon
//		break
//	case "droneoff":	// %%MIDI droneoff
//		break
//	case "gchord":		// %%MIDI gchord <list of letters and repeat numbers>
//				//	z rest
//				//	c chord
//				//	f fundamental
//				//	b fundamental + chord



//				// defaults:
//				//	M:2/4 or 4/4	fzczfzcz
//				//	M:3/4	fzczcz
//				//	M:6/8	fzcfzc
//				//	M:9/8	fzcfzcfzc
//		break
	case "gchordon":	// %%MIDI gchordon
	case "gchordoff":	// %%MIDI gchordoff
		if (!cfmt.chord)
			cfmt.chord = {}
		if (abc.parse.state >= 2
		 && curvoice) {
			s = abc.new_block("midigch")
			s.play = s.invis = 1 //true

			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--				// channel range 1..16 => 0..15
			if (abc.parse.state == 3) {
				s = abc.new_block("midiprog")
				s.play = s.invis = 1 //true
				curvoice.chn = s.chn = v
			} else {
				abc.set_v_param("channel", v)
			}
		break
	case "drummap":
//fixme: should have a 'MIDIdrum' per voice?
		v = Number(a[3])







|




>
>
>





|








>
|
|
>
>

>
>












|







118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//	case "drone":		// %%MIDI drone <#prog> <pit_1> <pit_2> <vol_1> <vol_2>
//				//	default: 70 45 33 80 80
//		break
//	case "droneon":		// %%MIDI droneon
//		break
//	case "droneoff":	// %%MIDI droneoff
//		break
	case "gchord":		// %%MIDI gchord <list of letters and repeat numbers>
//				//	z rest
//				//	c chord
//				//	f fundamental
//				//	b fundamental + chord
//				//	G/H/I/J/K	individual notes starting
//				//		from the lowest note of the chord
//				//	g/h/i/j/k	an octave above these
//				// defaults:
//				//	M:2/4 or 4/4	fzczfzcz
//				//	M:3/4	fzczcz
//				//	M:6/8	fzcfzc
//				//	M:9/8	fzcfzcfzc
		// fall thru
	case "gchordon":	// %%MIDI gchordon
	case "gchordoff":	// %%MIDI gchordoff
		if (!cfmt.chord)
			cfmt.chord = {}
		if (abc.parse.state >= 2
		 && curvoice) {
			s = abc.new_block("midigch")
			s.play = s.invis = 1 //true
			if (a[1][6] == 'o')
				s.on = a[1][7] == 'n'
			else
				s.rhy = a[2]		// chord rhythm
		} else if (a[1][6] == 'o') {
			cfmt.chord.gchon = a[1][7] == 'n'
		} else {
			cfmt.chord.rhy = a[2]
		}
		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--				// channel range 1..16 => 0..15
			if (abc.parse.state == 3) {
				s = abc.new_block("midiprog")
				s.play = s.invis = 1 //true
				s.chn = v
			} else {
				abc.set_v_param("channel", v)
			}
		break
	case "drummap":
//fixme: should have a 'MIDIdrum' per voice?
		v = Number(a[3])
︙︙
176
177
178
179
180
181
182
183
184
185



186

187
188

189
190
191
192
193
194
195
196
197
198


199
200
201
202
203
204
205
206
207
			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) {	// with a channel
			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 //true
			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







|
<
|
>
>
>

>
|

>
|
|






|
|
>
>

|







185
186
187
188
189
190
191
192

193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
			n += "'"
			v -= 12
		}
		this.do_pscom("map MIDIdrum " + a[2] + " play=" + n)
		abc.set_v_param("mididrum", "MIDIdrum")
		break
	case "program":
		a.shift()

		v = []
		if (a[2]) {
			v[0] = +a[2]		// program
			v[1] = +a[1]		// channel
		} else {
			v[0] = +a[1]
			v[1] = 0
		}
		if (isNaN(v[0]) || v[0] < 0 || v[0] > 127
		 || (v[1]
		  && (isNaN(v[1]) || v[1] <= 0 || v[1] > 16))) {
			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 //true
			s.instr = v[0]
			s.chn = v[1] > 0
				? (v[1] - 1)
				: curvoice.v < 9 ? curvoice.v : curvoice.v + 1
		} else {
			abc.set_v_param("instr", a.slice(1).join(' '))
		}
		break
	case "control":
		n = parseInt(a[2])
		if (isNaN(n) || n < 0 || n > 127) {
			abc.syntax(1, "Bad controller number in %%MIDI")
			break
︙︙
302
303
304
305
306
307
308
309
310
311
312
313
314

315

316
317
318
319
320
321
322
323
324
325
326
327
	of(a.slice(0))			// (copy because the parameters are removed)

	for (i = 0; i < a.length; i++) {
		switch (a[i]) {
		case "channel=":		// %%MIDI channel
			s = abc.new_block("midiprog")
			s.play = s.invis = 1 //true
			s.chn = curvoice.chn = a[++i]
			break
		case "instr=":			// %%MIDI program
			s = abc.new_block("midiprog")
			s.play = s.invis = 1 //true
			s.instr = a[++i]

			if (curvoice.chn == undefined) {

				curvoice.chn = curvoice.v < 9 ?
						curvoice.v :
						curvoice.v + 1
			}
			s.chn = curvoice.chn
			break
		case "midictl=":		// %%MIDI control
			if (!curvoice.midictl)
				curvoice.midictl = []
			item = a[++i].split(' ');
			curvoice.midictl[item[0]] = Number(item[1])
			break







|




|
>
|
>
|


<
|







317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335

336
337
338
339
340
341
342
343
	of(a.slice(0))			// (copy because the parameters are removed)

	for (i = 0; i < a.length; i++) {
		switch (a[i]) {
		case "channel=":		// %%MIDI channel
			s = abc.new_block("midiprog")
			s.play = s.invis = 1 //true
			s.chn = +a[++i]
			break
		case "instr=":			// %%MIDI program
			s = abc.new_block("midiprog")
			s.play = s.invis = 1 //true
			s.instr = a[++i].split(' ')
			if (s.instr[1])
				s.chn = s.instr.shift() - 1
			else
				s.chn = curvoice.v < 9 ?
						curvoice.v :
						curvoice.v + 1

			s.instr = +s.instr[0]
			break
		case "midictl=":		// %%MIDI control
			if (!curvoice.midictl)
				curvoice.midictl = []
			item = a[++i].split(' ');
			curvoice.midictl[item[0]] = Number(item[1])
			break
︙︙
Changes to modules/break.js.
1
2
3
4
5
6
7
8
9
10
// break.js - module to handle the %%break command
//
// Copyright (C) 2018-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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.


|







1
2
3
4
5
6
7
8
9
10
// break.js - module to handle the %%break command
//
// Copyright (C) 2018-2024 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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
︙︙
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
    var	abc2svg = {}

abc2svg.break = {

	// get the %%break parameters
	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 < a.length; n++) {
			b = a[n];







|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
    var	abc2svg = {}

abc2svg.break = {

	// get the %%break parameters
	get_break: function(parm) {
	    var	C = abc2svg.C,
		b, c, d, n, sq,
		a = parm.split(/[ ,]/),
		cfmt = this.cfmt()

		if (!cfmt.break)
			cfmt.break = []
		for (n = 1; n < a.length; n++) {
			b = a[n];
︙︙
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
					t: d[1] * C.BLEN / d[2],
					sq: sq})
		}
	}, // get_break()

	// insert the EOLs of %%break
	do_break: function() {
	    var	i, m, t, brk, seq,
		voice_tb = this.get_voice_tb()
		v = this.get_cur_sy().top_voice,
		s1 = voice_tb[v].sym,
		cfmt = this.cfmt()

		for (i = 0; i < cfmt.break.length; i++) {
			brk = cfmt.break[i];
			m = brk.m







|
|







62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
					t: d[1] * C.BLEN / d[2],
					sq: sq})
		}
	}, // get_break()

	// insert the EOLs of %%break
	do_break: function() {
	    var	i, m, t, brk, s, seq,
		voice_tb = this.get_voice_tb(),
		v = this.get_cur_sy().top_voice,
		s1 = voice_tb[v].sym,
		cfmt = this.cfmt()

		for (i = 0; i < cfmt.break.length; i++) {
			brk = cfmt.break[i];
			m = brk.m
︙︙
Changes to modules/chordnames.js.
︙︙
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.
//
// This module is loaded by %%chordnames.
//
// Parameters
// 1st syntax:
//		%%chordnames <comma separated list of chord names>
//	Each name replace one chord. The order is:
//		CDEFGAB<N.C.>
// 2nd syntax:
//		%%chordnames <comma separated list of key ':' value>
//	The key may be a chord letter ('A') with/or an accidental
//	Example:
//		%%chordnames Bb:B,B:H,b:s	% German chords








|







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.
//
// This module is loaded by %%chordnames.
//
// Parameters
// 1st syntax:
//		%%chordnames <comma separated list of chord names>
//	Each name replaces one chord. The order is:
//		CDEFGAB<N.C.>
// 2nd syntax:
//		%%chordnames <comma separated list of key ':' value>
//	The key may be a chord letter ('A') with/or an accidental
//	Example:
//		%%chordnames Bb:B,B:H,b:s	% German chords

︙︙
Changes to modules/clip.js.
1
2
3
4
5
6
7
8
9
10
// clip.js - module to handle the %%clip command
//
// Copyright (C) 2018-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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.


|







1
2
3
4
5
6
7
8
9
10
// clip.js - module to handle the %%clip command
//
// Copyright (C) 2018-2024 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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
︙︙
64
65
66
67
68
69
70
71

72
73



























74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
			return
		}
		this.cfmt().clip = [b, c]
    }, // get_clip()

    // cut the tune
    do_clip: function() {
    var	C = abc2svg.C

	voice_tb = this.get_voice_tb(),
	cfmt = this.cfmt()




























	// go to a global (measure + time)
	function go_global_time(s, sel) {
	    var	s2, bar_time, seq

		if (sel.m <= 1) {	// special case: there is no measure 0/1
			if (sel.m == 1) {
				for (s2 = s; s2; s2 = s2.ts_next) {
					if (s2.type == C.BAR
					 && s2.time != 0)
						break
				}
				if (s2.time < voice_tb[this.get_cur_sy().top_voice].
								meter.wmeasure)
					s = s2
			}
		} else {
			for ( ; s; s = s.ts_next) {
				if (s.type == C.BAR
				 && s.bar_num >= sel.m)







|
>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>












|







64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
			return
		}
		this.cfmt().clip = [b, c]
    }, // get_clip()

    // cut the tune
    do_clip: function() {
    var	C = abc2svg.C,
	abc = this,
	voice_tb = this.get_voice_tb(),
	cfmt = this.cfmt()

	// check the pointers (ties and slurs)
	function chkptr(last) {
	    var	i, sl, s2, m, nt,
		tim = last.time,
		s = abc.get_tsfirst()

		do {
			if (s.sls) {
				for (i = 0; i < s.sls.length; i++) {
					sl = s.sls[i]
					s2 = sl.se		// slur end
					if (s2.time >= tim)
						sl.loc = 'o'	// no end
				}
			}
			if (s.ti1			// if tie
			 && s.time + s.dur >= tim) {	// ending out of the clip seq.
				for (m = 0; m <= s.nhd; m++) {
					nt = s.notes[m]
					if (nt.tie_e && nt.tie_e.s.time >= tim)
						delete nt.tie_e	// no end
				}
			}
			s = s.ts_next
		} while (s)
	} // chkptr()

	// go to a global (measure + time)
	function go_global_time(s, sel) {
	    var	s2, bar_time, seq

		if (sel.m <= 1) {	// special case: there is no measure 0/1
			if (sel.m == 1) {
				for (s2 = s; s2; s2 = s2.ts_next) {
					if (s2.type == C.BAR
					 && s2.time != 0)
						break
				}
				if (s2.time < voice_tb[abc.get_cur_sy().top_voice].
								meter.wmeasure)
					s = s2
			}
		} else {
			for ( ; s; s = s.ts_next) {
				if (s.type == C.BAR
				 && s.bar_num >= sel.m)
︙︙
141
142
143
144
145
146
147

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167









168
169


170
171
172
173
174
175
176
			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 < voice_tb.length; v++) {
				p_voice = voice_tb[v]
				for (s2 = s; s2; s2 = s2.ts_next) {
					if (s2.v == v) {
						delete s2.prev
						break
					}
				}
				p_voice.sym = s2
			}









			this.set_tsfirst(s)
			delete s.ts_prev


		}

		/* remove the end of the tune */
		s = go_global_time(s, cfmt.clip[1])
		if (!s)
			return








>




















>
>
>
>
>
>
>
>
>
|
|
>
>







169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
			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)
					s2.p_v.ckey = this.clone(s2.as.u.ckey)
					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 < voice_tb.length; v++) {
				p_voice = voice_tb[v]
				for (s2 = s; s2; s2 = s2.ts_next) {
					if (s2.v == v) {
						delete s2.prev
						break
					}
				}
				p_voice.sym = s2
			}
			s2 = this.get_tsfirst()
			if (s != s2) {
				if (s2.type == C.STAVES) {
					s2.ts_next = s
					s.ts_prev = s2
					s2.next = s2.p_v.sym
					s2.p_v.sym = s2
					s2.next.prev = s2
				} else {
					this.set_tsfirst(s)
					delete s.ts_prev
				}
			}
		}

		/* remove the end of the tune */
		s = go_global_time(s, cfmt.clip[1])
		if (!s)
			return

︙︙
190
191
192
193
194
195
196


197
198
199
200
201
202
203
					break
				}
			}
			if (!s2)
				p_voice.sym = null
		}
		delete s.ts_prev.ts_next


	}, // do_clip()

    do_pscom: function (of, text) {
	if (text.slice(0, 5) == "clip ")
		abc2svg.clip.get_clip.call(this, text)
	else
		of(text)







>
>







230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
					break
				}
			}
			if (!s2)
				p_voice.sym = null
		}
		delete s.ts_prev.ts_next

		chkptr(s.ts_prev)	// check the pointers (ties and slurs)
	}, // do_clip()

    do_pscom: function (of, text) {
	if (text.slice(0, 5) == "clip ")
		abc2svg.clip.get_clip.call(this, text)
	else
		of(text)
︙︙
Changes to modules/combine.js.
︙︙
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
			if (s.invis && !s2.invis)
				return //false
		} else if (s.combine <= 2) {
			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







<
<







73
74
75
76
77
78
79


80
81
82
83
84
85
86
			if (s.invis && !s2.invis)
				return //false
		} else if (s.combine <= 2) {
			return //false
		}
		return true
	}


	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
︙︙
Changes to modules/gamelan.js.
1
2
3
4
5
6
7
8
9
10
// gamelan.js - module to output Gamelan (indonesian) music sheets
//
// Copyright (C) 2020-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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.


|







1
2
3
4
5
6
7
8
9
10
// gamelan.js - module to output Gamelan (indonesian) music sheets
//
// Copyright (C) 2020-2024 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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
︙︙
60
61
62
63
64
65
66

67
68
69
70
71
72
73
74
		break
	}
	of(p)
  },

// adjust some symbols before the generation
  output_music: function(of) {

    var	C = abc2svg.C,
	abc = this,
	cur_sy = abc.get_cur_sy(),
	voice_tb = abc.get_voice_tb()

	if (!abc.cfmt().gamelan) {
		of()
		return







>
|







60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
		break
	}
	of(p)
  },

// adjust some symbols before the generation
  output_music: function(of) {
    var	v,
	C = abc2svg.C,
	abc = this,
	cur_sy = abc.get_cur_sy(),
	voice_tb = abc.get_voice_tb()

	if (!abc.cfmt().gamelan) {
		of()
		return
︙︙
Changes to modules/grid2.js.
1
2
3
4
5
6
7
8
9
10
// grid2.js - module to replace a voice in the music by a chord grid
//
// Copyright (C) 2018-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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.


|







1
2
3
4
5
6
7
8
9
10
// grid2.js - module to replace a voice in the music by a chord grid
//
// Copyright (C) 2018-2024 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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
︙︙
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
			if (voice_tb[v].grid2 == undefined)
				voice_tb[v].grid2 = 1

	for (v = 0; v < voice_tb.length; v++) {
		p_v = voice_tb[v]
		if (!p_v.grid2)
			continue
		curvoice = p_v
		this.set_v_param("stafflines", "...")	// no staff
		p_v.clef.invis = true;		// no clef
		p_v.key.k_sf = 0		// no key signature
		delete p_v.key.k_a_acc
		p_v.staffnonote = 2		// draw the staff
		for (s = p_v.sym; s; s = s.next) {
			delete s.a_dd		// no decoration







|







38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
			if (voice_tb[v].grid2 == undefined)
				voice_tb[v].grid2 = 1

	for (v = 0; v < voice_tb.length; v++) {
		p_v = voice_tb[v]
		if (!p_v.grid2)
			continue
		this.set_curvoice(p_v)
		this.set_v_param("stafflines", "...")	// no staff
		p_v.clef.invis = true;		// no clef
		p_v.key.k_sf = 0		// no key signature
		delete p_v.key.k_a_acc
		p_v.staffnonote = 2		// draw the staff
		for (s = p_v.sym; s; s = s.next) {
			delete s.a_dd		// no decoration
︙︙
Changes to modules/grid3.js.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32


33
34
35
36
37
38
39
// grid3.js - module to insert a manual chords
//
// Copyright (C) 2020-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 Lesser 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.
//
// This module is loaded when "%%begingrid" appears in a ABC source.
//
// Parameters
//	%%begingrid [ chord-define [ noprint ] ]
//	    list of chords, '-' or '.', measure bars ('|') and ':' for repeat
//	%%endgrid
//
//	%%gridfont font_name size (default: 'serif 16')
//
// When this command appears inside a tune and when 'chord-define' is present,
// the chords are used to define the chord symbols that are displayed
// above the staff system.
// When 'noprint' is also present, the grid itself is not displayed.



if (typeof abc2svg == "undefined")
    var	abc2svg = {}

abc2svg.grid3 = {

// generate the grid


|



















|









>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// grid3.js - module to insert a manual chords
//
// Copyright (C) 2020-2024 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 Lesser 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.
//
// This module is loaded when "%%begingrid" appears in a ABC source.
//
// Parameters
//	%%begingrid [ chord-define [ noprint ] ] [ lm=<left margin> ] [ width=<width> ]
//	    list of chords, '-' or '.', measure bars ('|') and ':' for repeat
//	%%endgrid
//
//	%%gridfont font_name size (default: 'serif 16')
//
// When this command appears inside a tune and when 'chord-define' is present,
// the chords are used to define the chord symbols that are displayed
// above the staff system.
// When 'noprint' is also present, the grid itself is not displayed.
//
// The left margin may be '-1' to center the grid (default).

if (typeof abc2svg == "undefined")
    var	abc2svg = {}

abc2svg.grid3 = {

// generate the grid
︙︙
118
119
120
121
122
123
124




125






126
127
128
129
130
131
132
133
134
135
136
137

		// -- build_grid() --

		// build the content of the cells
		hr = font.size * 2.1
		if (wc < hr * 1.4)
			wc = hr * 1.4			// cell width




		w = wc * nc				// grid width







		// generate the cells
		yl = posy + 3
		y = posy + 3 - font.size * .7
		x0 = (img.width / cfmt.scale - w) / 2
		while (1) {
			cl = cells.shift()
			if (!cl)
				break
			y += hr
			yl += hr
			x = x0 + wc / 2







>
>
>
>
|
>
>
>
>
>
>




<







120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

142
143
144
145
146
147
148

		// -- build_grid() --

		// build the content of the cells
		hr = font.size * 2.1
		if (wc < hr * 1.4)
			wc = hr * 1.4			// cell width
		if (s.width) {
			w = s.width
			wc = w / nc
		} else {
			w = wc * nc			// grid width
		}
		x0 = s.lm < 0
			? (img.width / cfmt.scale - w) / 2	// center
			: s.lm + 1
		if ((s.fmt || cfmt).trimsvg)		// adjust the SVG width
			img.wx = x0 + w - img.lm - img.rm

		// generate the cells
		yl = posy + 3
		y = posy + 3 - font.size * .7

		while (1) {
			cl = cells.shift()
			if (!cl)
				break
			y += hr
			yl += hr
			x = x0 + wc / 2
︙︙
265
266
267
268
269
270
271
272


273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289






290
291



















292

293
294
295
296


297
298
299
300


301
302
303
304
305
306
307
308
309
310
311
312
313
314
	// build the grid and insert it in the music
	build_grid()
	abc.blk_flush()
    }, // block_gen()

// handle %%begingrid
    do_begin_end: function(of, type, opt, txt) {
    var	vt = this.get_voice_tb()



	if (type != "grid") {
		of(type, opt, txt)
		return
	}

	// replace the accidentals
	txt = txt.replace(/#|=|b/g,
		function(x) {
			switch (x) {
			case '#': return "\u266f"
			case '=': return "\u266e"
//			case 'b': return "\u266d"
			}
			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


						})
		}
	}
    }, // do_begin_end()

    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 = []








|
>
>

















>
>
>
>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>




>
>



|
>
>






|







276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
	// build the grid and insert it in the music
	build_grid()
	abc.blk_flush()
    }, // block_gen()

// handle %%begingrid
    do_begin_end: function(of, type, opt, txt) {
    var	i, s, v,
	lm = -1,		// left margin - default: center the grid
	width			// grid width - default: computed

	if (type != "grid") {
		of(type, opt, txt)
		return
	}

	// replace the accidentals
	txt = txt.replace(/#|=|b/g,
		function(x) {
			switch (x) {
			case '#': return "\u266f"
			case '=': return "\u266e"
//			case 'b': return "\u266d"
			}
			return "\u266d"
		})

	opt = opt.trim().split(/\s+/)
	while (1) {
		i = opt.shift()
		if (!i)
			break
		switch (i[0]) {
		case 'c':			// chord-define
			this.cfmt().csdef = txt
			break
		case 'n':			// noprint
			type = ""
			break
		case 'l':			// lm=..
		case 'w':			// width=..
			v = i.match(/-?[\d.]+.?.?/)
			if (v) {
				v = this.get_unit(v[0])
				if (!isNaN(v)) {
					if (i[0] == 'l')
						lm = v
					else
						width = v
				}
			}
			break
		}
	}

	if (type) {
		type += "3"
		if (this.parse.state >= 2) {
			s = this.new_block(type)
			s.text = txt
			s.lm = lm
			s.width = width
		} else {
			abc2svg.grid3.block_gen.call(this, null, {
						subtype: type,
						text: txt,
						lm: lm,
						width: width
						})
		}
	}
    }, // do_begin_end()

    output_music: function(of) {
    var	ln, i, dt, ss, ntim, p_vc, s3, cl,
	C = abc2svg.C,
	abc = this,
	s = abc.get_tsfirst(),
	vt = abc.get_voice_tb(),
	t = abc.cfmt().csdef,
	cs = []

︙︙
Changes to modules/jianpu.js.
︙︙
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
		}
	}

	of()
  }, // output_music()

  draw_symbols: function(of, p_voice) {
    var	s, s2, nl, y,
	C = abc2svg.C,
	abc = this,
	dot = "\ue1e7",
	anno_a = abc.anno_a,
	staff_tb = abc.get_staff_tb(),
	out_svg = abc.out_svg,
	out_sxsy = abc.out_sxsy,
	xypath = abc.xypath

	if (!p_voice.jianpu) {
		of(p_voice)
		return
	}

	// draw the duration lines under the notes
	function draw_dur(s1, x, y, s2, n, nl) {
	    var s, s3,
		sc = s1.grace ? .5 : 1

		xypath(x - 3, y + 5)
		out_svg('h' + ((s2.x - s1.x) / sc + 8).toFixed(1) + '"/>\n')	// "
		y -= 2.5
		while (++n <= nl) {
			s = s1
			while (1) {
				if (s.nflags && s.nflags >= n) {
					s3 = s
					while (s != s2) {







|
















|
<


|







354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378

379
380
381
382
383
384
385
386
387
388
		}
	}

	of()
  }, // output_music()

  draw_symbols: function(of, p_voice) {
    var	s, s2, g, nl, y,
	C = abc2svg.C,
	abc = this,
	dot = "\ue1e7",
	anno_a = abc.anno_a,
	staff_tb = abc.get_staff_tb(),
	out_svg = abc.out_svg,
	out_sxsy = abc.out_sxsy,
	xypath = abc.xypath

	if (!p_voice.jianpu) {
		of(p_voice)
		return
	}

	// draw the duration lines under the notes
	function draw_dur(s1, x, y, s2, n, nl) {
	    var s, s3


		xypath(x - 3, y + 5)
		out_svg('h' + (s2.x - s1.x + 8).toFixed(1) + '"/>\n')
		y -= 2.5
		while (++n <= nl) {
			s = s1
			while (1) {
				if (s.nflags && s.nflags >= n) {
					s3 = s
					while (s != s2) {
︙︙
398
399
400
401
402
403
404









































405
406
407
408
409
410
411
				}
				if (s == s2)
					break
				s = s.next
			}
		}
	} // draw_dur()










































	function out_mus(x, y, p) {
		out_svg('<text x="')
		out_sxsy(x, '" y="', y)
		out_svg('">' + p + '</text>\n')
	} // out_mus()








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
				}
				if (s == s2)
					break
				s = s.next
			}
		}
	} // draw_dur()

	// draw the duration lines of grace notes
	// and the slurs
	// (assume all notes have the same duration)
	function draw_dgr(s) {
	    var	g, x, l,
		y = staff_tb[s.st].y,
		s2 = s.extra,
		nl = s2.nflags

		for (g = s2; g.next; g = g.next)
			;
		out_svg('<g transform="translate(')
		out_sxsy(s2.x, ',', y + 19)	// (font height + 4)
		out_svg(') scale(.5)" stroke-width="1.5">\n')
		abc.stv_g().g++			// in container
		l = ((g.x - s2.x) * 2 + 8).toFixed(1)
		y = (nl > 0 ? 5 * nl : 0) + 2
		if (nl > 0) {
			xypath(-3, 0)
			while (--nl >= 0) {
				out_svg('m0 4h' + l)
				l = -l
			}
			out_svg('"/>\n')
		}
		if (s.fmt.graceslurs		// if a slur, 
		 && !s.sl1) {			// but not an explicit one
			l = Math.abs(l)
			out_svg('<path class="stroke" \
d="m' + (l / 2 - 3).toFixed(1)+' '+y.toFixed(1))
			if (s.gr_shift) {
				out_svg('m0 0c0 10 0 10 -10 10')
			} else {
				out_svg('m0 0c0 10 0 10 10 10')
			}
			out_svg('"/>\n')
		}
		out_svg('</g>\n')
		abc.stv_g().g--
	} // draw_dgr()

	function out_mus(x, y, p) {
		out_svg('<text x="')
		out_sxsy(x, '" y="', y)
		out_svg('">' + p + '</text>\n')
	} // out_mus()

︙︙
500
501
502
503
504
505
506
507
508
509
510
511
512


513
514
515
516



517
518
519
520
521
522
523
		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
		}
	}
  }, // draw_symbols()

// set some parameters
    set_fmt: function(of, cmd, param) {







|





>
>




>
>
>







540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
		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 = s.next
				if (s.nflags > nl)
					nl = s.nflags
				if (s.beam_end)
					break
				if (s.type == C.GRACE)
					draw_dgr(s)
			}
			if (s.dy)
				y += s.dy
			draw_dur(s2, s2.x, y, s, 1, nl)
			break
		case C.GRACE:
			draw_dgr(s)
			break
		}
	}
  }, // draw_symbols()

// set some parameters
    set_fmt: function(of, cmd, param) {
︙︙
Changes to modules/page.js.
︙︙
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
	}
	return fh * a[4]
    }, // gen_hf()

    // start a new page
    open_page: function(page,
			ht) {	// spacing under the header
    var	h, l,
	abc = page.abc,
	cfmt = abc.cfmt(),
	sty = '<div style="line-height:0'

	page.pn++
	page.pna++








|







247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
	}
	return fh * a[4]
    }, // gen_hf()

    // start a new page
    open_page: function(page,
			ht) {	// spacing under the header
    var	h,
	abc = page.abc,
	cfmt = abc.cfmt(),
	sty = '<div style="line-height:0'

	page.pn++
	page.pna++

︙︙
272
273
274
275
276
277
278
279


280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302


303
304
305
306
307
308
309
310
311

	ht += page.topmargin
	page.hmax = cfmt.pageheight - page.botmargin - ht

	// define the header/footer
	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)		// new style(s)
		if (cfmt.fullsvg || sty != page.hsty) {
			page.hsty = sty
			sty = '<style>' + sty + '\n</style>\n'
		} else {
			sty = ''
		}
		abc2svg.page.svg_tag(cfmt.pagewidth, ht + h, "header")
		abc2svg.page.user_out(sty +
			'<g transform="translate(0,' +
				page.topmargin.toFixed(1) + ')">\n' +
				page.hf + '</g>\n</svg>')
		page.hmax -= h;
		page.hf = ''
	} else {
		abc2svg.page.svg_tag(cfmt.pagewidth, ht, "header")
		abc2svg.page.user_out('\n</svg>')
	}
	if (page.footer) {
		l = abc.get_font_style().length


		page.fh = abc2svg.page.gen_hf(page, "footer")
		sty = abc.get_font_style().slice(l)		// new style(s)
		if (cfmt.fullsvg || sty != page.fsty) {
			page.fsty = sty
			page.ffsty = '<style>' + sty + '\n</style>\n'
		} else {
			page.ffsty = ''
		}
		page.hmax -= page.fh







|
>
>



|


















|
>
>

|







272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315

	ht += page.topmargin
	page.hmax = cfmt.pageheight - page.botmargin - ht

	// define the header/footer
	page.hf = ''
	if (page.header) {
		abc.clr_sty()
		if (!cfmt.headerfont)
			abc.param_set_font("headerfont", "text,serif 16")
		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()			// new style(s)
		if (cfmt.fullsvg || sty != page.hsty) {
			page.hsty = sty
			sty = '<style>' + sty + '\n</style>\n'
		} else {
			sty = ''
		}
		abc2svg.page.svg_tag(cfmt.pagewidth, ht + h, "header")
		abc2svg.page.user_out(sty +
			'<g transform="translate(0,' +
				page.topmargin.toFixed(1) + ')">\n' +
				page.hf + '</g>\n</svg>')
		page.hmax -= h;
		page.hf = ''
	} else {
		abc2svg.page.svg_tag(cfmt.pagewidth, ht, "header")
		abc2svg.page.user_out('\n</svg>')
	}
	if (page.footer) {
		abc.clr_sty()
		if (!cfmt.footerfont)
			abc.param_set_font("footerfont", "text,serif 16")
		page.fh = abc2svg.page.gen_hf(page, "footer")
		sty = abc.get_font_style()			// new style(s)
		if (cfmt.fullsvg || sty != page.fsty) {
			page.fsty = sty
			page.ffsty = '<style>' + sty + '\n</style>\n'
		} else {
			page.ffsty = ''
		}
		page.hmax -= page.fh
︙︙
429
430
431
432
433
434
435

436
437
438
439
440
441
442
				abc: this,
				topmargin: 38,	// 1cm
				botmargin: 38,	// 1cm
//				gutter: 0,
				h: 0,		// current page height
				pn: 0,		// page number
				pna: 0,		// absolute page number

				first: true	// no skip to next page
			}

			// don't let the backend handle the header/footer
			if (cfmt.header) {
				page.header = cfmt.header;
				cfmt.header = null







>







433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
				abc: this,
				topmargin: 38,	// 1cm
				botmargin: 38,	// 1cm
//				gutter: 0,
				h: 0,		// current page height
				pn: 0,		// page number
				pna: 0,		// absolute page number
				ffsty: '',	// style of the footer
				first: true	// no skip to next page
			}

			// don't let the backend handle the header/footer
			if (cfmt.header) {
				page.header = cfmt.header;
				cfmt.header = null
︙︙
Changes to modules/strftime.js.
1
2

3
4
5
6
7
8
9
/* Port of strftime() by T. H. Doan (https://thdoan.github.io/strftime/)
 *

 * Day of year (%j) code based on Joe Orost's answer:
 * http://stackoverflow.com/questions/8619879/javascript-calculate-the-day-of-the-year-1-366
 *
 * Week number (%V) code based on Taco van den Broek's prototype:
 * http://techblog.procurios.nl/k/news/view/33796/14863/calculate-iso-8601-week-and-year-in-javascript.html
 */
// Copyright (c) 2016 Tom Doan - License MIT
|
<
>







1

2
3
4
5
6
7
8
9
// Port of strftime() by T. H. Doan (https://thdoan.github.io/strftime/)

/*
 * Day of year (%j) code based on Joe Orost's answer:
 * http://stackoverflow.com/questions/8619879/javascript-calculate-the-day-of-the-year-1-366
 *
 * Week number (%V) code based on Taco van den Broek's prototype:
 * http://techblog.procurios.nl/k/news/view/33796/14863/calculate-iso-8601-week-and-year-in-javascript.html
 */
// Copyright (c) 2016 Tom Doan - License MIT
︙︙
Changes to modules/strtab.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - strtab.js - tablature for string instruments
//
// Copyright (C) 2020-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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.


|







1
2
3
4
5
6
7
8
9
10
// abc2svg - strtab.js - tablature for string instruments
//
// Copyright (C) 2020-2024 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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
︙︙
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

	// draw the note heads
	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('<text class="bg' + abc.bgn +
				'" x="')
			abc.out_sxsy(x, '" y="', stb + y - 2.5)
			abc.out_svg('">' + not.nb + '</text>\n')
		}







|


|







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

	// draw the note heads
	function draw_heads(stb, s) {
	    var	m, not, x, y

		for (m = 0; m <= s.nhd; m++) {
			not = s.notes[m]
			if (!not.nb)
				continue
			x = s.x - 3
			if (not.nb.length > 1)
				x -= 3
			y = 3 * (not.pit - 18)
			abc.out_svg('<text class="bg' + abc.bgn +
				'" x="')
			abc.out_sxsy(x, '" y="', stb + y - 2.5)
			abc.out_svg('">' + not.nb + '</text>\n')
		}
︙︙
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277

278
279
280
281

282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299



300





301
302
303
304
305
306
307
		this.set_v_param("minfret", parm)
		return
	}
	of(cmd, parm)
    }, // set_fmt()

    // change the notes when the global generation settings are done
    set_stems: function(of) {
    var	p_v, i, m, nt, n, bi, bn, strss, g,
	C = abc2svg.C,
	abc = this,
	s = abc.get_tsfirst(),		// first symbol
	strs = [],			// notes per staff - index = staff
	lstr = []			// lowest string per staff - index staff

	// set a string (pitch) and a fret number
	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





		}
	} // set_pit()

	function set_notes(p_v, s) {
	    var	i, bi, bn, nt, m, n, ns

		s.stem = -1			// down stems







|









>
|


|
>

|


|






|
|
|
|
|
|
|
>
>
>
|
>
>
>
>
>







261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
		this.set_v_param("minfret", parm)
		return
	}
	of(cmd, parm)
    }, // set_fmt()

    // change the notes when the global generation settings are done
    set_glue: function(of, width) {
    var	p_v, i, m, nt, n, bi, bn, strss, g,
	C = abc2svg.C,
	abc = this,
	s = abc.get_tsfirst(),		// first symbol
	strs = [],			// notes per staff - index = staff
	lstr = []			// lowest string per staff - index staff

	// set a string (pitch) and a fret number
	function set_pit(p_v, s, nt, i) {
	    var	m,
		st = s.st

		if (i >= 0) {
			nt.nb = ((p_v.diafret ? nt.pit : nt.midi) - p_v.tab[i])
						.toString()
			if (p_v.diafret && nt.acc)
				nt.nb += '+'
			nt.pit = i * 2 + 18
		} else {
			nt.nb = ""
			nt.pit = 18
		}
		nt.acc = 0
		nt.invis = true
		if (!s.grace)
			strss[i] = s.time + s.dur
	    if (p_v.pos.stm != C.SL_HIDDEN) {
		if (!lstr[st])
			lstr[st] = [ 10, null, C.BLEN ]
		if (lstr[st][0] > i) {
			lstr[st][0] = i		// lowest string
			lstr[st][1] = s
		}
		if (s.dur < lstr[st][2])
			lstr[st][2] = s.dur
	    }
		s.stemless = 1 //true
		if (s.dots) {			// have nicer dots
			s.xmx = 0
			for (m = 0; m <= s.nhd; m++)
				s.notes[m].shhd = 0
			s.dot_low = 0
		}
	} // set_pit()

	function set_notes(p_v, s) {
	    var	i, bi, bn, nt, m, n, ns

		s.stem = -1			// down stems
︙︙
331
332
333
334
335
336
337
338

339

340
341
342
343
344
345
346
			nt = s.notes[m]
			if (nt.sls) {			// if start of slur
				for (i = 0; i < nt.sls.length; i++) {
					nt.sls[i].ty &= ~0x07
					nt.sls[i].ty |= C.SL_ABOVE
				}
			}
			if (nt.nb != undefined)

				continue

			if (nt.a_dd) {
				i = nt.a_dd.length
				while (--i >= 0) {
					bi = strnum(nt.a_dd[i].name)
					if (bi >= 0) {
						set_pit(p_v, s, nt, bi)
						delete nt.a_dd







|
>

>







341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
			nt = s.notes[m]
			if (nt.sls) {			// if start of slur
				for (i = 0; i < nt.sls.length; i++) {
					nt.sls[i].ty &= ~0x07
					nt.sls[i].ty |= C.SL_ABOVE
				}
			}
			if (nt.nb) {
				delete nt.a_dd
				continue
			}
			if (nt.a_dd) {
				i = nt.a_dd.length
				while (--i >= 0) {
					bi = strnum(nt.a_dd[i].name)
					if (bi >= 0) {
						set_pit(p_v, s, nt, bi)
						delete nt.a_dd
︙︙
368
369
370
371
372
373
374

375

376
377
378
379
380
381
382
					bn = n
				}
			}
			set_pit(p_v, s, nt, bi)
		}

		s.y = 3 * (nt.pit - 18)

		s.ymn = 0		// don't get space below the tablature

	} // set_notes()

	// get the string number from the decoration
	// format is either !<n>s! or !<n>!
	function strnum(n) {
		n = n.match(/^([1-9])s?$/)
		return n ? p_v.tab.length - n[1] : -1







>
|
>







380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
					bn = n
				}
			}
			set_pit(p_v, s, nt, bi)
		}

		s.y = 3 * (nt.pit - 18)

 		// if no stem, don't get space below the tablature
		s.ymn =	s.stemless ? -12 : 0
	} // set_notes()

	// get the string number from the decoration
	// format is either !<n>s! or !<n>!
	function strnum(n) {
		n = n.match(/^([1-9])s?$/)
		return n ? p_v.tab.length - n[1] : -1
︙︙
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
		m = p_v[n].capo
		if (m) {
			for (i = 0; i < p_v[n].tab.length; i++)
				p_v[n].tab[i] += m
		}
	}

	of()				// do the normal work

	// loop on the notes of the voices with a tablature
	for ( ; s; s = s.ts_next) {

		// let a stem on the lowest string
		if (s.seqst || (s.ts_prev && s.ts_prev.type == C.GRACE)) {
			for (i = 0; i < lstr.length; i++) {
				if (lstr[i]) {
					lstr[i][1].tabst = 1
					lstr[i] = null
				}
			}
		}

		p_v = s.p_v
		if (!p_v.tab)
			continue
		strss = strs[s.st]







|







|

|
<







404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421

422
423
424
425
426
427
428
		m = p_v[n].capo
		if (m) {
			for (i = 0; i < p_v[n].tab.length; i++)
				p_v[n].tab[i] += m
		}
	}

	of(width)				// do the normal work

	// loop on the notes of the voices with a tablature
	for ( ; s; s = s.ts_next) {

		// let a stem on the lowest string
		if (s.seqst || (s.ts_prev && s.ts_prev.type == C.GRACE)) {
			for (i = 0; i < lstr.length; i++) {
				if (lstr[i] && lstr[i][2] < C.BLEN)
					lstr[i][1].tabst = 1
				lstr[i] = null

			}
		}

		p_v = s.p_v
		if (!p_v.tab)
			continue
		strss = strs[s.st]
︙︙
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439




440
441
442
443
444
445
446
447
		default:
			delete s.a_dd
			break
		case C.GRACE:
			if (p_v.pos.gst == C.SL_HIDDEN)
				s.sappo = 0
			for (g = s.extra; g; g = g.next) {
				if (p_v.pos.gst == C.SL_HIDDEN)
					g.stemless = true
				set_notes(p_v, g)
			}
			break
		case C.NOTE:
			set_notes(p_v, s)
			break
		}
	}




    }, // set_stems()

    // get the parameters of the current voice
    set_vp: function(of, a) {
    var	i, e, g, tab, strs, ok,
	p_v = this.get_curvoice()

	// convert a list of ABC notes into a list of MIDI pitches







<
<








>
>
>
>
|







436
437
438
439
440
441
442


443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
		default:
			delete s.a_dd
			break
		case C.GRACE:
			if (p_v.pos.gst == C.SL_HIDDEN)
				s.sappo = 0
			for (g = s.extra; g; g = g.next) {


				set_notes(p_v, g)
			}
			break
		case C.NOTE:
			set_notes(p_v, s)
			break
		}
	}
	for (i = 0; i < lstr.length; i++) {
		if (lstr[i] && lstr[i][2] < C.BLEN)
			lstr[i][1].tabst = 1		// top of stem
	}
    }, // set_glue()

    // get the parameters of the current voice
    set_vp: function(of, a) {
    var	i, e, g, tab, strs, ok,
	p_v = this.get_curvoice()

	// convert a list of ABC notes into a list of MIDI pitches
︙︙
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
				tab = abc2tab(strs)		// ABC syntax
			if (!tab) {
				this.syntax(1, "Bad strings in tablature")
				ok = false
			}
		} else if (!p_v.tab) {
			tab = p_v.diafret ?
				[10, 14, 17] :		// dulcimer
				[40, 45, 50, 55, 59, 64] // guitar strings
		} else {
			tab = p_v.tab
		}
	}
	if (ok) {
		if (p_v.capo) {







|







630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
				tab = abc2tab(strs)		// ABC syntax
			if (!tab) {
				this.syntax(1, "Bad strings in tablature")
				ok = false
			}
		} else if (!p_v.tab) {
			tab = p_v.diafret ?
				[17, 14, 10] :		// dulcimer
				[40, 45, 50, 55, 59, 64] // guitar strings
		} else {
			tab = p_v.tab
		}
	}
	if (ok) {
		if (p_v.capo) {
︙︙
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
	of(a)
    }, // set_vp()

    set_hooks: function(abc) {
	abc.draw_symbols = abc2svg.strtab.draw_symbols.bind(abc, abc.draw_symbols)
	abc.gch_build = abc2svg.strtab.csan_bld.bind(abc, abc.gch_build)
	abc.set_format = abc2svg.strtab.set_fmt.bind(abc, abc.set_format);
	abc.set_stems = abc2svg.strtab.set_stems.bind(abc, abc.set_stems)
	abc.set_vp = abc2svg.strtab.set_vp.bind(abc, abc.set_vp)

	// define specific decorations used to force the string number
    var	decos = abc.get_decos()
	decos["1s"] = "0 nul 0 0 0"
	decos["2s"] = "0 nul 0 0 0"
	decos["3s"] = "0 nul 0 0 0"
	decos["4s"] = "0 nul 0 0 0"
	decos["5s"] = "0 nul 0 0 0"
	decos["6s"] = "0 nul 0 0 0"
	if (!user.nul)
		user.nul = function() {}

	abc.add_style("\n.bn{font:bold 7px sans-serif}")
    } // set_hooks()
} // strtab

if (!abc2svg.mhooks)
	abc2svg.mhooks = {}
abc2svg.mhooks.strtab = abc2svg.strtab.set_hooks







|




|
|
|
|
|
|
<
<








689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706


707
708
709
710
711
712
713
714
	of(a)
    }, // set_vp()

    set_hooks: function(abc) {
	abc.draw_symbols = abc2svg.strtab.draw_symbols.bind(abc, abc.draw_symbols)
	abc.gch_build = abc2svg.strtab.csan_bld.bind(abc, abc.gch_build)
	abc.set_format = abc2svg.strtab.set_fmt.bind(abc, abc.set_format);
	abc.set_sym_glue = abc2svg.strtab.set_glue.bind(abc, abc.set_sym_glue)
	abc.set_vp = abc2svg.strtab.set_vp.bind(abc, abc.set_vp)

	// define specific decorations used to force the string number
    var	decos = abc.get_decos()
	decos["1s"] = "0 nil 0 0 0"
	decos["2s"] = "0 nil 0 0 0"
	decos["3s"] = "0 nil 0 0 0"
	decos["4s"] = "0 nil 0 0 0"
	decos["5s"] = "0 nil 0 0 0"
	decos["6s"] = "0 nil 0 0 0"



	abc.add_style("\n.bn{font:bold 7px sans-serif}")
    } // set_hooks()
} // strtab

if (!abc2svg.mhooks)
	abc2svg.mhooks = {}
abc2svg.mhooks.strtab = abc2svg.strtab.set_hooks
Added modules/tempo.js.








































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// abc2svg - temponame.js - change/create the beats per minute of a tempo
//
// Copyright (C) 2024 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 Lesser 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.
//
// This module is loaded by %%temponame.
//
// Parameters
//	%%temponame <tempo name> <beats per minute>
// The names of the tempos are in the table abc2svg.tmp_tb (cf util/sndgen.js)

if (typeof abc2svg == "undefined")
    var	abc2svg = {}

abc2svg.temponame = {

    set_fmt: function(of, cmd, parm) {
    var	r

	if (cmd == "temponame") {
		r = /("[^"]+"|\w+)\s+(\d+)/.exec(parm)	// "
		if (r)
			abc2svg.tmp_tb[r[1]] = r[2]
//		else
//			error
		return
	}
	of(cmd, parm)
    }, // set_fmt()

    set_hooks: function(abc) {
	abc.set_format = abc2svg.temponame.set_fmt.bind(abc, abc.set_format)
    } // set_hooks()
} // temponame

if (!abc2svg.mhooks)
	abc2svg.mhooks = {}
abc2svg.mhooks.temponame = abc2svg.temponame.set_hooks
Changes to modules/tropt.js.
︙︙
111
112
113
114
115
116
117


118
119
120
121
122
123
124
					break
				default:
					continue
				}
				if (ok(s, np)) {
					nt.pit = np
					nt.acc = na


				}
			}
		}
	}
	of(last_s)
    }, // voice_adj()








>
>







111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
					break
				default:
					continue
				}
				if (ok(s, np)) {
					nt.pit = np
					nt.acc = na
					if (p_v.map)
						this.set_map(p_v, nt, na)
				}
			}
		}
	}
	of(last_s)
    }, // voice_adj()

︙︙
Deleted package.json.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
{
  "name": "abc2svg",
  "version": "1.22.1",
  "description": "ABC music notation translator",
  "files": [
    "COPYING",
    "COPYING.LESSER",
    "MIDI-1.js",
    "Scc1t2/",
    "abc2odt",
    "abc2svg",
    "abc2svg-1.js",
    "abc2svg.ttf",
    "abcdoc-1.js",
    "abcemb-1.js",
    "abcemb1-1.js",
    "abcemb2-1.js",
    "abcjsc",
    "abcqjs",
    "abcmjs",
    "abcnode",
    "abctopdf",
    "abcweb-1.js",
    "abcweb1-1.js",
    "abcweb2-1.js",
    "ambitus-1.js",
    "break-1.js",
    "capo-1.js",
    "chord-1.js",
    "chordnames-1.js",
    "clair-1.js",
    "clip-1.js",
    "cmdline.js",
    "combine-1.js",
    "diag-1.js",
    "edit-1.css",
    "edit-1.js",
    "edit-1.xhtml",
    "edit-de.js",
    "edit-en.js",
    "edit-fr.js",
    "edit-it.js",
    "edit-pt_BR.js",
    "equalbars-1.js",
    "err-de.js",
    "err-en.js",
    "err-fr.js",
    "err-it.js",
    "err-pt_BR.js",
    "gamelan-1.js",
    "grid-1.js",
    "grid2-1.js",
    "grid3-1.js",
    "json-1.js",
    "jazzchord-1.js",
    "jianpu-1.js",
    "mdnn-1.js",
    "nns-1.js",
    "page-1.js",
    "pedline-1.js",
    "perc-1.js",
    "play-1.js",
    "psvg-1.js",
    "roman-1.js",
    "snd-1.js",
    "soloffs-1.js",
    "sth-1.js",
    "strtab-1.js",
    "temper-1.js",
    "toabc.js",
    "toaudio-1.js",
    "toabw.js",
    "tohtml.js",
    "tomei.js",
    "tonotes.js",
    "toodt.js",
    "toparam.js",
    "toxhtml.js"
  ],
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "bin": {
    "abc2odt": "abc2odt",
    "abc2svg": "abc2svg",
    "abcjsc": "abcjsc",
    "abcqjs": "abcqjs",
    "abcmjs": "abcmjs",
    "abcnode": "abcnode",
    "abctopdf": "abctopdf"
  },
  "repository": {
    "type": "fossil",
    "url": "https://chiselapp.com/user/moinejf/repository/abc2svg"
  },
  "keywords": [
    "ABC",
    "music",
    "SVG"
  ],
  "author": "Jean-François Moine",
  "license": "LGPL-3.0",
  "bugs": {
    "url": "https://chiselapp.com/user/moinejf/repository/abc2svg/reportlist"
  },
  "homepage": "https://chiselapp.com/user/moinejf/repository/abc2svg"
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































































































































































































Changes to toabc.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - toabc.js - convert ABC to ABC
//
// Copyright (C) 2016-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.


|







1
2
3
4
5
6
7
8
9
10
// abc2svg - toabc.js - convert ABC to ABC
//
// Copyright (C) 2016-2024 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.
︙︙
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
					}
					t = ly.t
					if (ly.ln) {
						w[i] += t
						if (ly.ln == 1)
							w[i] += '-'
					} else {
						t = t.replace(/ /g, '~')
						t = t.replace(/-/g, '\\-')
						if (t.slice(-1) != "-")
							t += ' '
						w[i] += t
					}
				}
			}







|







455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
					}
					t = ly.t
					if (ly.ln) {
						w[i] += t
						if (ly.ln == 1)
							w[i] += '-'
					} else {
						t = t.replace(/ /g, '~')
						t = t.replace(/-/g, '\\-')
						if (t.slice(-1) != "-")
							t += ' '
						w[i] += t
					}
				}
			}
︙︙
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
//fixme: put 'X' if more than one measure
			if (s.time > vti[v] + 2) {
				line += 'x' + dur_dump(s.time - vti[v])
			}
			vti[v] = s.time
		}
		sym_dump(s)
		if (s.dur)
			vti[v] = s.time + s.dur
		if (s.next) {
			if (s.beam_end && !s.beam_st && !s.next.beam_end)
				line += ' '
		}
		if (line)
			vo[v] += line
	}
	voice_out();
	lyric_dump();







|

|
|







984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
//fixme: put 'X' if more than one measure
			if (s.time > vti[v] + 2) {
				line += 'x' + dur_dump(s.time - vti[v])
			}
			vti[v] = s.time
		}
		sym_dump(s)
		if (s.dur) {
			vti[v] = s.time + s.dur
			if (s.dur < C.BLEN / 4 && s.beam_end
			 && s.next && s.next.dur && s.next.dur < C.BLEN / 4)
				line += ' '
		}
		if (line)
			vo[v] += line
	}
	voice_out();
	lyric_dump();
︙︙
Added tosvg.js.








































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// txtmus - tosvg.js - SVG generation
//
// Copyright (C) 2024 Jean-Francois Moine
//
// This file is part of txtmus.
//
// txtmus 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.
//
// txtmus 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 txtmus.  If not, see <http://www.gnu.org/licenses/>.

    var	fn,
	out = [],			// output without SVG container
	yo = 0,				// offset of the next SVG
	w = 0				// max width

if (!tm)
    var	tm = abc2svg

// fix the problem about the text multi-coordinates in librsvg
function bug(p) {
    var	i, t, r, x, y, c, o,
	j = 0

	while (1) {
		i = p.indexOf("<text x=", j)
		if (i < 0)
			return p
		j = p.indexOf("</text", i)
		t = p.slice(i, j)

		r = t.match(/x="([^"]+)"\s+y="([^"]+)"[^>]*>(.+)/)
			// r[1] = x list, r[2] = y list, r[3] = characters

		if (!r || r[1].indexOf(',') < 0)
			continue
		x = r[1].split(',')
		y = r[2].split(',')
		k = 0
		o = '<text x="' + x[0] + '" y="' + y[0] + '">' + r[3][0]
		while (++k < x.length)
			o += '\n<tspan x="' + x[k] + '" y="' + y[k] + '">'
				+ r[3][k] + '</tspan>'
		p = p.replace(t, o)
	}
	// not reached
} //bug()

// entry point from cmdline
tm.abc_init =
tm.mus_init = function(args) {
	user.img_out = function(p) {
	    var	i, h

		switch (p.slice(0, 4)) {
		case "<svg":
			h = p.match(/width="(\d+)px" height="(\d+)px"/)
			if (w < h[1])
				w = h[1]	// max width
			p = bug(p)
			i = p.indexOf('>')
			out.push(p.slice(0, i)
				+ '\n y="' + yo + '"'
				+ p.slice(i))
			yo += +h[2]		// offset next SVG
			break
		default:
			if (p.slice(-6) == "</svg>")
				out.push("</svg>")
			break
		}
	}

	// get the main music source file name
	for (var i = 0; i < args.length; i++) {
		fn = args[i]
		if (!fn || fn[0] != '-')
			break
		i++
	}
} // mus_init()

tm.abc_end =
tm.mus_end = function() {
	tm.print('<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
 xmlns:xlink="http://www.w3.org/1999/xlink"\n\
 width="' + w + 'px" height="' + yo + 'px">\n'
		+ out.join('\n')
		+ '\n</svg>')
	if (user.errtxt)
		tm.printErr(user.errtxt)
}
Changes to util/abcweb.js.
1
2
3
4
5
6
7
8
9
10
// abcweb-1.js file to include in html pages
//
// 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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.


|







1
2
3
4
5
6
7
8
9
10
// abcweb-1.js file to include in html pages
//
// Copyright (C) 2014-2024 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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
︙︙
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
				alert('error loading ' + fn)
		}
		document.head.appendChild(s)
	} // loadjs()

// function called when abcweb-1.js is fully loaded
function dom_loaded() {
    var	abc, src, outb, err,
	a_inc = {},
	tune_lst = [],	// array of [tsfirst, voice_tb, info, cfmt] per tune
			// created on playback start
	html,			// set if some pure HTML in the file
	busy,
	playing,
	playconf = {
		onend: function() {
			playing = 0 //false
		}
	}

	// check which <div> became visible
	// if yes, generate it
	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)
			}







|















|



|
|

|


|







87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
				alert('error loading ' + fn)
		}
		document.head.appendChild(s)
	} // loadjs()

// function called when abcweb-1.js is fully loaded
function dom_loaded() {
    var	abc, src, outb, err, e,
	a_inc = {},
	tune_lst = [],	// array of [tsfirst, voice_tb, info, cfmt] per tune
			// created on playback start
	html,			// set if some pure HTML in the file
	busy,
	playing,
	playconf = {
		onend: function() {
			playing = 0 //false
		}
	}

	// check which <div> became visible
	// if yes, generate it
	function visible() {
	    var	i, r,
		wh = window.innerHeight || document.documentElement.clientHeight

		while (1) {
			i = abc2svg.alldiv[0]
			if (i == undefined)
				break
			r = abc2svg.music[i].d.getBoundingClientRect()
			if (r.top > wh)
				break
			musgen(i)
			abc2svg.alldiv.shift()
		}
		if (abc2svg.alldiv.length) {
			if (!abc2svg.onscroll) {
				abc2svg.onscroll = visible
				window.addEventListener("scroll", visible)
			}
︙︙
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
		s = e.style

		// from class=".."
		c = e.getAttribute("class")
		if (c) {
			c = '.' + c				// selector
			for (i = 0; i < sh.length; i++) {
				r = sh[i].rules
				for (j = 0; j < r.length; j++) {
					if (r[j].selectorText == c)
						break
				}
				if (j < r.length)
					break
			}







|







139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
		s = e.style

		// from class=".."
		c = e.getAttribute("class")
		if (c) {
			c = '.' + c				// selector
			for (i = 0; i < sh.length; i++) {
				r = sh[i].cssRules
				for (j = 0; j < r.length; j++) {
					if (r[j].selectorText == c)
						break
				}
				if (j < r.length)
					break
			}
︙︙
272
273
274
275
276
277
278

279
280
281
282
283
284
285
286
287
288

289
290
291
292
293
294
295
296

297
298
299
300
301
302
303
304
305
306
307

308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323


324

325
326
327
328
329

330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
					break
			}
			if (i >= sa.length)
				break

			c = get_p(s)			// get custom properties
			div = document.createElement('div')

			if (s.text.indexOf('\nX:') < 0) {
				abc2svg.music[0].t += c + s.innerHTML // global
				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_music()

	// generate a music sequence
	// @mu = object { t: music source, d: <div> }
	function musgen(mu) {

	    var	t = mu.t

		if (busy) {
			mu.w = 1 //true
			return
		}
		busy = 1 //true

		// render a music sequence
		function render() {
		    var	i, j, e


			// start the generation
			outb = err = ""
			abc.tosvg(mu.n, t)	// music source

			abc2svg.abc_end()	// close the page if %%pageheight

			// mu.d can be null when parameters in query string
			// and no ABC script with global parameters
		    if (mu.d) {

			if (err)
				outb += '<pre class="nop" style="background:#ff8080">'
					+ err + "</pre>\n"

			if (abc.cfmt().with_source && outb)


				outb = '<pre class="source">'

					+ clean_txt(t)
					+ '</pre>\n\
<div class="source">\n'
					+ outb
					+ '</div>\n'

			
			mu.d.innerHTML = outb	// update the browser

			mu.d.addEventListener('click', abc2svg.playseq)
			e = mu.d.getElementsByTagName('svg')
			for (i = 0; i < e.length; i++) {
				j = e[i].getAttribute('class')
				if (!j)
					continue	// (page formatting)
				j = j.match(/tune(\d+)/)
				if (!j)
					continue
				j = j[1]		// tune number
				tune_lst[j] = null	// get new play references
			}
		    } // if (mu.d)

			// if some generation waiting, start it
			mu.w = busy = 0 //false
			for (i = 1; i < abc2svg.music.length; i++) {
				if (abc2svg.music[i].w) {
					musgen(abc2svg.music[i])
					break
				}
			}
		} // render()

		// load the possible %%abc-include files
		function include() {







>
|









>







|
>
|









|
>





<
<








|
>
>
|
>





>



|
<
<
<
<
<
<
<
<
<
<
<






|







272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316


317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339











340
341
342
343
344
345
346
347
348
349
350
351
352
353
					break
			}
			if (i >= sa.length)
				break

			c = get_p(s)			// get custom properties
			div = document.createElement('div')
			if (!abc2svg.music[0].t
			 && s.text.indexOf('\nX:') < 0) {
				abc2svg.music[0].t += c + s.innerHTML // global
				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
				})
				div.addEventListener('click', abc2svg.playseq)
			}
			s.parentNode.replaceChild(div, s)
		}
	} // save_music()

	// generate a music sequence
	// @mu = object { t: music source, d: <div> }
	function musgen(ix) {
	    var	mu = abc2svg.music[ix],
		t = mu.t

		if (busy) {
			mu.w = 1 //true
			return
		}
		busy = 1 //true

		// render a music sequence
		function render() {
		    var	i, j, e, o,
			f = abc.cfmt().with_source

			// start the generation
			outb = err = ""
			abc.tosvg(mu.n, t)	// music source



			// mu.d can be null when parameters in query string
			// and no ABC script with global parameters
		    if (mu.d) {

			if (err)
				outb += '<pre class="nop" style="background:#ff8080">'
					+ err + "</pre>\n"

			if (f && outb) {
				o = f.indexOf('edit') > 0
					? " contentEditable>" : ">"
				outb = '<pre class="source"'
					+ o
					+ clean_txt(t)
					+ '</pre>\n\
<div class="source">\n'
					+ outb
					+ '</div>\n'
			}
			
			mu.d.innerHTML = outb	// update the browser

			abc2svg.abc_end(ix)	// end of page (for %%pageheight, abcweb)











		    } // if (mu.d)

			// if some generation waiting, start it
			mu.w = busy = 0 //false
			for (i = 1; i < abc2svg.music.length; i++) {
				if (abc2svg.music[i].w) {
					musgen(i)
					break
				}
			}
		} // render()

		// load the possible %%abc-include files
		function include() {
︙︙
391
392
393
394
395
396
397



398
399
400
401
402
403
404
				a_inc[fn] = '%\n'
				alert('Error getting ' + fn + '\n' + r.statusText)
				include()
			}
			r.send()
		} // include()




		// load the required modules and the include files before rendering
		if (abc2svg.modules.load(t, include))
			include()
	} // musgen()

abc2svg.musgen = musgen			// used in set_music() below








>
>
>







386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
				a_inc[fn] = '%\n'
				alert('Error getting ' + fn + '\n' + r.statusText)
				include()
			}
			r.send()
		} // include()

		// musgen entry
		abc = abc2svg.abc	// reload the Abc instance after edition

		// load the required modules and the include files before rendering
		if (abc2svg.modules.load(t, include))
			include()
	} // musgen()

abc2svg.musgen = musgen			// used in set_music() below

︙︙
504
505
506
507
508
509
510










511
512
513
514
515
516
517

518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547















548
549
550
551








552
553
554
555
556
557
558
559

	// load the abc2svg core if not done by <script>
	src = document.body.innerHTML
	if (!abc2svg.Abc) {
		abc2svg.loadjs("abc2svg-1.js", dom_loaded)
		return
	}











	if (src.indexOf('type="text/vnd.abc"') < 0)
		move_music(src)

	save_music()	// save the music sequences and replace them by <div>

	// initialize the generation

	abc = new abc2svg.Abc(user)
	if (typeof follow == "function")	// if snd-1.js loaded
		follow(abc, user, playconf)	// initialize the play follow
	if (abc2svg.music[0].t)
		musgen(abc2svg.music[0])	// global definitions

	// create a list of all <div>'s
	abc2svg.alldiv = []
	for (var i = 1; i < abc2svg.music.length; i++)
		abc2svg.alldiv.push(abc2svg.music[i])
	visible()
} // dom_loaded()

// ---- interface for changing the music source and redisplaying it ----
// get the music source of a <div> element
abc2svg.get_music = function(d) {
    var	i, mu

	for (var i = 1; i < abc2svg.music.length; i++) {
		mu = abc2svg.music[i]
		if (mu.d == d)
			return mu.t
	}
//	return ""
} // get_music()

// set the music source of a <div> element and redisplay it
abc2svg.set_music = function(d, t) {
    var	i, mu
















	for (var i = 1; i < abc2svg.music.length; i++) {
		mu = abc2svg.music[i]
		if (mu.d == d) {
			mu.t = t








			abc2svg.musgen(mu)
			break
		}
	}
} // set_music()

// wait for the scripts to be loaded
window.addEventListener("load", dom_loaded, {once:true})







>
>
>
>
>
>
>
>
>
>







>




|




|


















|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|



>
>
>
>
>
>
>
>
|







502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591

	// load the abc2svg core if not done by <script>
	src = document.body.innerHTML
	if (!abc2svg.Abc) {
		abc2svg.loadjs("abc2svg-1.js", dom_loaded)
		return
	}

	// create a hidden span for string width computation
	e = document.createElement("span")
	e.style.position = "absolute"
	e.style.top =
		e.style.padding = 0
	e.style.visibility = "hidden"
	e.style.lineHeight = 1
	document.body.appendChild(e)
	abc2svg.el = e

	if (src.indexOf('type="text/vnd.abc"') < 0)
		move_music(src)

	save_music()	// save the music sequences and replace them by <div>

	// initialize the generation
	abc2svg.abc =				// for external access
	abc = new abc2svg.Abc(user)
	if (typeof follow == "function")	// if snd-1.js loaded
		follow(abc, user, playconf)	// initialize the play follow
	if (abc2svg.music[0].t)
		musgen(0)			// global definitions

	// create a list of all <div>'s
	abc2svg.alldiv = []
	for (var i = 1; i < abc2svg.music.length; i++)
		abc2svg.alldiv.push(i)
	visible()
} // dom_loaded()

// ---- interface for changing the music source and redisplaying it ----
// get the music source of a <div> element
abc2svg.get_music = function(d) {
    var	i, mu

	for (var i = 1; i < abc2svg.music.length; i++) {
		mu = abc2svg.music[i]
		if (mu.d == d)
			return mu.t
	}
//	return ""
} // get_music()

// set the music source of a <div> element and redisplay it
abc2svg.set_music = function(d, t) {
    var	i, mu, tunes

	// remove the tune data that will not be used anymore
	function tune_purge(mu) {
	    var	svg, cl, i,
		e = mu.d.getElementsByTagName('svg')

		for (i = 0; i < e.length; i++) { // look at the SVGs of the sequence
			cl = e[i].getAttribute("class")		// SVG class
			if (cl)
				cl = cl.match(/tune(\d+)/)	// tune number
			if (!cl)
				continue
			tunes[cl[1]] = null
		}
	} // tune_purge()

	for (i = 1; i < abc2svg.music.length; i++) {
		mu = abc2svg.music[i]
		if (mu.d == d) {
			mu.t = t
			tunes = abc2svg.abc.tunes
			tune_purge(mu)
			abc2svg.abc = new abc2svg.Abc(user) // use a new Abc instance

			// copy the old tunes to the new instance
			abc2svg.abc.tunes.push.apply(abc2svg.abc.tunes, tunes)
			if (abc2svg.music[0].t)
				abc2svg.musgen(0)	// global definitions
			abc2svg.musgen(i)
			break
		}
	}
} // set_music()

// wait for the scripts to be loaded
window.addEventListener("load", dom_loaded, {once:true})
Changes to util/abcweb1.js.
1
2
3
4
5
6
7
8
9
10
// abcweb1-1.js file to include in html pages with abc2svg-1.js
//
// Copyright (C) 2019-2022 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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.


|







1
2
3
4
5
6
7
8
9
10
// abcweb1-1.js file to include in html pages with abc2svg-1.js
//
// Copyright (C) 2019-2024 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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
︙︙
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

    var user
if (typeof abc2svg == "undefined")
    var abc2svg = {}

// function called when abc2svg is fully loaded
function dom_loaded() {
    var	abc,
	new_page,
	playing,
	abcplay,

	tune_dur,			// scroll tune duration
	scroll_to,			// scroll timeout
	dt,				// scroll delta per timeout







|







56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

    var user
if (typeof abc2svg == "undefined")
    var abc2svg = {}

// function called when abc2svg is fully loaded
function dom_loaded() {
    var	abc, e,
	new_page,
	playing,
	abcplay,

	tune_dur,			// scroll tune duration
	scroll_to,			// scroll timeout
	dt,				// scroll delta per timeout
︙︙
312
313
314
315
316
317
318

319
320
321
322
323
324
325

		// aweful hack: user.anno_stop must be defined before Abc creation
		// for being set later by follow() !
		if (typeof follow == "function")
			user.anno_stop = function(){}

		tune_lst = []

		abc = new abc2svg.Abc(user)
		new_page = ""

		// initialize the play follow function
		if (typeof follow == "function")
			follow(abc, user, playconf)








>







312
313
314
315
316
317
318
319
320
321
322
323
324
325
326

		// aweful hack: user.anno_stop must be defined before Abc creation
		// for being set later by follow() !
		if (typeof follow == "function")
			user.anno_stop = function(){}

		tune_lst = []
		abc2svg.abc =			// for external access
		abc = new abc2svg.Abc(user)
		new_page = ""

		// initialize the play follow function
		if (typeof follow == "function")
			follow(abc, user, playconf)

︙︙
510
511
512
513
514
515
516










517
518
519
520
521
522
523
524
525
526
527
				return
			}
		}

		playing = true
		abcplay.play(s, null)
	} // click()











	// accept page formatting
	abc2svg.abc_end = function() {}

	// load the required modules, then render the music
	if (abc2svg.modules.load(page, include))
		include()
} // dom_loaded()

// wait for the scripts to be loaded
window.addEventListener("load", dom_loaded)







>
>
>
>
>
>
>
>
>
>











511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
				return
			}
		}

		playing = true
		abcplay.play(s, null)
	} // click()

	// create a hidden span for string width computation
	e = document.createElement("span")
	e.style.position = "absolute"
	e.style.top =
		e.style.padding = 0
	e.style.visibility = "hidden"
	e.style.lineHeight = 1
	document.body.appendChild(e)
	abc2svg.el = e

	// accept page formatting
	abc2svg.abc_end = function() {}

	// load the required modules, then render the music
	if (abc2svg.modules.load(page, include))
		include()
} // dom_loaded()

// wait for the scripts to be loaded
window.addEventListener("load", dom_loaded)
Changes to util/chord.js.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

21
22
23
24
25
26
27
28


29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74


75
76
77
78
79
80
81

82
83
84
85
86
87
88

89






90





91
92
93







94
95



96

97
98
99
100
101
102
103

104

105
106
107


108

109
110
111
112
113


114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129


130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// chord.js - generation of accompaniment
//
// Copyright (C) 2020-2022 Jean-Francois Moine and Seymour Shlien
//
// This file is part of abc2svg.
//
// abc2svg is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.

// -- chord table --

// index = chord symbol type
// value: array of strings
//	index = inversion
//	string = list of 2 characters
//		1st character = note (see abc2svg.letmid)
//		2nd character = octave ('+', ' ', '-')
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 "],
//	plus:	["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+"]
} // ch_names

abc2svg.midlet = "CdDeEFgGaAbB"		// MIDI pitch -> letter
abc2svg.letmid = {			// letter -> MIDI pitch
	C: 0,
	d: 1,
	D: 2,
	e: 3,
	E: 4,
	F: 5,
	g: 6,
	G: 7,
	a: 8,
	A: 9,
	b: 10,
	B: 11
} // letmid

abc2svg.chord = function(first,		// first symbol in time
			 voice_tb,	// table of the voices
			 cfmt) {	// tune parameters
    var	chnm, i, k, vch, s, gchon,


	C = abc2svg.C,
	trans = 48 + (cfmt.chord.trans ? cfmt.chord.trans * 12 : 0)

	// create a chord according to the bass note
	function chcr(b, ch) {
	    var	i, v,
		r = []


		b = abc2svg.midlet[b]
		i = ch.length
		while (--i > 0) {
			if (ch[i][0] == b)	// search the bass in the chord
				break
		}

		ch = ch[i]






		for (i = 0; i < ch.length; i += 2) {





			v = abc2svg.letmid[ch[i]]
			switch (ch[i + 1]) {
			case '+': v += 12; break







			case '-': v -= 12; break
			}



			r.push(v)

		}
		return r
	} // chcr()

	// get the playback part of the first chord symbol
	function filter(a_cs) {
	    var	i, cs, t



		for (i = 0; i < a_cs.length; i++) {
			cs = a_cs[i]
			if (cs.type != 'g')


				continue

			t = cs.otext
			if (t.slice(-1) == ')')		// if alternate chord
				t = t.replace(/\(.*/, '') // remove it
			return t.replace(/\(|\)|\[|\]/g,'') // remove ()[]
		}


	} // filter()

	// generate a chord
	function gench(sb) {
	    var	r, ch, b, m, n, not,
		a = filter(sb.a_gch),
		s = {
			v: vch.v,
			p_v: vch,
			type: C.NOTE,
			time: sb.time,
			notes: []
		}

		if (!a)
			return


		a = a.match(/([A-GN])([#♯b♭]?)([^/]*)\/?(.*)/)
			// a[1] = note, a[2] = acc, a[3] = type, a[4] = bass
		if (!a)
			return

		r = abc2svg.letmid[a[1]]		// root
		if (r == undefined) {
			if (a[1] != "N")
				return
			s.type = C.REST			// ("N") = no chord
			ch = [0]
			r = 0
		} else {
			switch (a[2]) {
			case "#":
			case "♯": r++; break
			case "b":
			case "♭": r--; break
			}
			if (!a[3]) {


|

















>

|

<
<
<
|
|
>
>
|
|
|
|
|
|
|
|
|
|
|
|
<
<
|
<
>
|
<
<
|
|
|
|
|
<
|
|


<

<


<

<

<






|
>
>





|
|
>

<
|
|
|
|
|
>
|
>
>
>
>
>
>
|
>
>
>
>
>
|
|
|
>
>
>
>
>
>
>
|
<
>
>
>
|
>

|
|

|
|
|
>

>
|
|
|
>
>

>
|
<
<
<

>
>
|


|
|
|
<
<
<
<
<
<
|
|
<
<
>
>
|





|
<
|
|
<
<
<







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40


41

42
43


44
45
46
47
48

49
50
51
52

53

54
55

56

57

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128



129
130
131
132
133
134
135
136
137






138
139


140
141
142
143
144
145
146
147
148

149
150



151
152
153
154
155
156
157
// chord.js - generation of accompaniment
//
// Copyright (C) 2020-2024 Jean-Francois Moine and Seymour Shlien
//
// This file is part of abc2svg.
//
// abc2svg is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.

// -- chord table --
// from https://en.wikipedia.org/wiki/Chord_(music)
// index = chord symbol type
// value: array of MIDI pitch / root
//	index = inversion



abc2svg.chnm = {
	'': [0, 4, 7],
	'6': [0, 4, 7, 9],
	'7': [0, 4, 7, 10],
	M7: [0, 4, 7, 11],
	aug: [0, 4, 8],
	aug7: [0, 4, 8, 10],
	m: [0, 3, 7],
	m6: [0, 3, 7, 9],
	m7: [0, 3, 7, 10],
	mM7: [0, 3, 7, 11],
	dim: [0, 3, 6],
	dim7: [0, 3, 6, 9],
	m7b5: [0, 3, 6, 10],
	'9': [0, 4, 7, 10, 14],
//	m9:


//	maj9:

//	M9:
	'11': [0, 4, 7, 10, 14, 17],


	sus4: [0, 5, 7]
//	sus9:
//	'7sus4':
//	'7sus9':
//	'5':

}

abc2svg.letmid = {			// letter -> MIDI pitch
	C: 0,

	D: 2,

	E: 4,
	F: 5,

	G: 7,

	A: 9,

	B: 11
} // letmid

abc2svg.chord = function(first,		// first symbol in time
			 voice_tb,	// table of the voices
			 cfmt) {	// tune parameters
    var	chnm, i, k, vch, s, gchon, s_ch, rhy, ti, dt,
	md = first.p_v.meter.wmeasure,	// measure duration
	nextim = 0,
	C = abc2svg.C,
	trans = 48 + (cfmt.chord.trans ? cfmt.chord.trans * 12 : 0)

	// create a chord according to the bass note
	function chcr(b, ch) {
	    var	t,
		r = ch.slice(),
		i = r.length


		if (b) {
			while (--i > 0) {
				if (r[i] == b)		// search the bass in the chord
					break
			}
			if (i > 0)
				r[0] = b, r[i] = 0
			else
				r.unshift(b)
		}
		r[0] -= 12			// bass one octave lower
		return r
	} // chcr()

	// return a new chord rhythm from a meter (M:)
	function meterhy(s) {
	    var	r,
		t = s.a_meter[0].top,
		b = s.a_meter[0].bot

		switch (b) {
		case '2':
			b = 4
			t *= 2
			// fall thru
		case undefined:
			if (t[0] == 'C')
				t = 4, b = 4
			// fall thru
		case '4':

			if (t == '3')
				return "fzczcz"
			return "fzczfzczfzcz".slice(0, t * 2)
		case '8':
			return "fzcfzcfzcfzc".slice(0, t)
		}
		return 'z'			// no chord
	} // meterhy()

	// build a chord rhythm
	function bld_rhy(p) {
	    var	i, c,
		j = 0

		rhy = p[0]
		for (i = 1; i < p.length; i++) {
			c = p[i]
			if (c >= '1' && c <= '9') {
				while (--c > 0)
					rhy += '+'	// continue playing
				continue
			}
			rhy += c



		}

		dt = md / rhy.length		// delta time
	} // bld_rhy()

	// generate a chord
	function gench(sb, i) {
	    var	r, ch, b, m, n, nt,
		a = sb.a_gch[i].otext







		if (a.slice(-1) == ')')			// if alternate chord


			a = a.replace(/\(.*/, '')	// remove it
		a = a.replace(/\(|\)|\[|\]/g,'')	// remove ()[]
		a = a.match(/([A-G])([#♯b♭]?)([^/]*)\/?(.*)/)
			// a[1] = note, a[2] = acc, a[3] = type, a[4] = bass
		if (!a)
			return

		r = abc2svg.letmid[a[1]]		// root
		if (r == undefined)			// "N" or no chord

			return




			switch (a[2]) {
			case "#":
			case "♯": r++; break
			case "b":
			case "♭": r--; break
			}
			if (!a[3]) {
︙︙
162
163
164
165
166
167
168



169
170
171
172
173
174
175
176
177
178
179
180
181
182


183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
















































































200
201
202

203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248











249







250
251
252

253
254
255
256
257











258
259
260

261






262
263
264
265


266
267
268
269
270
271
272
273
274
275
276
277

278
279


280
				if (b != undefined) {
					switch (a[4][1]) {
					case "#":
					case "♯": b++; if (b >= 12) b = 0; break
					case "b":
					case "♭": b--;  if (b < 0) b = 11; break
					}



				}
			}
		}
		if (b == undefined)
			b = 0
		ch = chcr(b, ch)

		// generate the notes of the chord
		n = ch.length
		r += trans
		if (sb.p_v.tr_snd)
			r += sb.p_v.tr_snd
		for (m = 0; m < n; m++) {
			not = {


				midi: r + ch[m]
			}
			s.notes.push(not)
		}
		s.nhd = n - 1

		// insert the chord in the chord voice and in the tune
		s.prev = vch.last_sym
		vch.last_sym.next = s
		s.ts_next = sb.ts_next
		sb.ts_next = s
		s.ts_prev = sb
		if (s.ts_next)
			s.ts_next.ts_prev = s
		vch.last_sym = s
	} // gench()

















































































	// -- chord() --

	// set the chordnames defined by %%MIDI chordname

	if (cfmt.chord.names) {
		chnm = Object.create(abc2svg.ch_names)
		for (k in cfmt.chord.names) {
			vch = ""
			for (i = 0; i < cfmt.chord.names[k].length; i++) {
				s = cfmt.chord.names[k][i]
				vch += abc2svg.midlet[s % 12]
				vch += i == 0 ? "-" :
					(s >= 12 ? "+" : " ")
			}
//fixme: no inversion
			chnm[k] = [ vch ]
		}
	} else {
		chnm = abc2svg.ch_names
	}

	// define the MIDI channel
	k = 0
	for (i = 0; i < voice_tb.length; i++) {
		if (k < voice_tb[i].chn)
			k = voice_tb[i].chn
	}
	if (k == 8)
		k++			// skip the channel 10

	// create the chord voice
	vch = {
		v: voice_tb.length,
		id: "_chord",
		time: 0,
		sym: {
			type: C.BLOCK,
			subtype: "midiprog",
			chn: k + 1,
			instr: cfmt.chord.prog || 0,
			time: 0,
			ts_prev: first,
			ts_next: first.ts_next
		},
		vol: cfmt.chord.vol || .6	// (external default 76.2)
	}
	vch.sym.p_v = vch
	vch.sym.v = vch.v
	vch.last_sym = vch.sym
	voice_tb.push(vch)











	first.ts_next = vch.sym








	// loop on the symbols and add the accompaniment chords
	gchon = cfmt.chord.gchon

	s = first
	while (1) {
		if (!s.ts_next) {
			if (gchon)
				vch.last_sym.dur = s.time - vch.last_sym.time











			break
		}
		s = s.ts_next

		if (!s.a_gch) {






			if (s.subtype == "midigch") {
				if (gchon && !s.on)
					vch.last_sym.dur = s.time - vch.last_sym.time
				gchon = s.on


			}
			continue
		}
		if (!gchon)
			continue
		for (i = 0; i < s.a_gch.length; i++) {
			gch = s.a_gch[i]
			if (gch.type != 'g')
				continue
			vch.last_sym.dur = s.time - vch.last_sym.time
			gench(s)
			break

		}
	}


} // chord()







>
>
>


<
<
<








|
>
>
|
|
<
<
|
<
<
<
<
<
<
<
<
<
<


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



>

<

|
|
|
<
<
<
|
<
<
<
<
<








|













|
<







>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>



>


<
|
|
>
>
>
>
>
>
>
>
>
>
>
|
|
<
>
|
>
>
>
>
>
>
|
|
<
|
>
>

<

|
<
<
<
<
<
<
<

>
|
<
>
>

170
171
172
173
174
175
176
177
178
179
180
181



182
183
184
185
186
187
188
189
190
191
192
193
194


195










196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282

283
284
285
286



287





288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310

311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342

343
344
345
346
347
348
349
350
351
352
353
354
355
356
357

358
359
360
361
362
363
364
365
366
367

368
369
370
371

372
373







374
375
376

377
378
379
				if (b != undefined) {
					switch (a[4][1]) {
					case "#":
					case "♯": b++; if (b >= 12) b = 0; break
					case "b":
					case "♭": b--;  if (b < 0) b = 11; break
					}
					b = b - r
					if (b < 0)
						b += 12
				}
			}



		ch = chcr(b, ch)

		// generate the notes of the chord
		n = ch.length
		r += trans
		if (sb.p_v.tr_snd)
			r += sb.p_v.tr_snd
		for (m = 0; m < n; m++) {
			nt = s_ch.notes[m]
			if (!nt)
				s_ch.notes[m] = nt = []
			nt.midi = r + ch[m]
		}


		s_ch.nhd = n - 1










	} // gench()

	// stop the previous chord by setting its duration
	function set_dur(s2, tim) {			// previous chord
		if (s2.dur
		 || tim == s2.time
		 || s2.nhd == undefined)	// no chord yet
			return
		s2.dur = tim - s2.time
		for (var m = 0; m <= s2.nhd; m++)
			s2.notes[m].dur = s2.dur
	} // set_dur()

	// insert a chord in the chord voice
	function insch(tim) {
		if (s_ch.nhd == undefined)
			return			// no defined chord yet
	    var	s, m,
		s2 = vch.last_sym,
		i = rhy[ti++]

		switch (i) {
		case '+':			// same chord
			if (rhy != '+')
				return
			i = 'b'			// no rhythm
			ti = 0
			break
		case undefined:
		case 'z':
			set_dur(s2, tim)	// stop the previous chord
			return
		}

		s = {
			v: vch.v,
			p_v: vch,
			type: C.NOTE,
			notes: []
		}
		s.nhd = s_ch.nhd
//		s.dur = s_ch.dur
		for (m = 0; m <= s.nhd; m++)
			s.notes[m] = {
//				dur: s_ch.notes[m].dur,
				midi: s_ch.notes[m].midi
			}
		s.time = tim
		switch (i) {
		case 'c':
			s.notes.shift()		// remove the root note
			s.nhd--
			break
		default:
			i = "GHIJKghijk".indexOf(rhy[ti])
			if (i < 0			// bad character
			 || i >= s.nhd)			// out of chord
				return
			s.notes[0] = s.notes[i % 5]
			if (i >= 5)
				s.notes[0].midi += 12	// upper octave
			// fall thru
		case 'f':
			s.nhd = 0		// keep the chord root
//			break
		case 'b':
			break
		}
		s.prev = s2			// previous chord
		s2.next = s
		set_dur(s2, tim)		// stop the last chord
		
		vch.last_sym = s
		while (s2.time < tim
		    && s2.ts_next)
			s2 = s2.ts_next
		s.ts_prev = s2.ts_prev
		s.ts_prev.ts_next = s
		s.ts_next = s2
		s2.ts_prev = s
	} // insch()

	// -- chord() --

	// set the chordnames defined by %%MIDI chordname
	chnm = abc2svg.chnm
	if (cfmt.chord.names) {

		for (k in cfmt.chord.names) {
			chnm[k] = []
			for (i = 0; i < cfmt.chord.names[k].length; i++)
				chnm[k].push(+cfmt.chord.names[k][i])



		}





	}

	// define the MIDI channel
	k = 0
	for (i = 0; i < voice_tb.length; i++) {
		if (k < voice_tb[i].chn)
			k = voice_tb[i].chn
	}
	if (k == 9)
		k++			// skip the channel 10

	// create the chord voice
	vch = {
		v: voice_tb.length,
		id: "_chord",
		time: 0,
		sym: {
			type: C.BLOCK,
			subtype: "midiprog",
			chn: k + 1,
			instr: cfmt.chord.prog || 0,
			time: 0,
			dur: 0

		},
		vol: cfmt.chord.vol || .6	// (external default 76.2)
	}
	vch.sym.p_v = vch
	vch.sym.v = vch.v
	vch.last_sym = vch.sym
	voice_tb.push(vch)

	s = first
	bld_rhy(cfmt.chord.rhy			// chord rhythm
		|| meterhy(s.p_v.meter))

	// insert the MIDI program of the chord voice after the tempo
	while (s.type != C.TEMPO && !s.dur)
		s = s.ts_next
	vch.sym.ts_prev = s
	vch.sym.ts_next = s.ts_next
	s.ts_next.ts_prev = vch.sym
	s.ts_next = vch.sym

	s_ch = {				// chord template
//		v: vch.v,
//		p_v: vch,
//		type: C.NOTE,
		notes: []
	}

	// loop on the symbols and add the accompaniment chords
	gchon = cfmt.chord.gchon
	ti = 0					// time index in rhy
	s = first
	while (1) {

		if (gchon && rhy != '+') {
			while (s.time > nextim) {
				insch(nextim)	// generate the rhythm
				nextim += dt
			}
		}
		if (s.a_gch) {
			for (i = 0; i < s.a_gch.length; i++) {
				if (s.a_gch[i].type != 'g')
					continue
				gench(s, i)
				if (rhy == '+')
					insch(s.time)	// no rhythm, start now
				break
			}

		}
		if (!s.dur) {
			if (s.bar_num) {		// if measure bar
				ti = 0			// reset the time index
			} else if (s.wmeasure) {	// if meter
				md = s.wmeasure
				if (rhy != '+')
					bld_rhy(meterhy(s))
			} else if (s.subtype == "midigch") {
				if (s.on != undefined)

					gchon = s.on
				else
					bld_rhy(s.rhy)	// new rhythm
			}

		}
		if (!s.ts_next)







			break
		s = s.ts_next
	}

	if (gchon)
		set_dur(vch.last_sym, s.time)
} // chord()
Changes to util/follow.js.
︙︙
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
	abc.out_svg('<rect class="abcr _' + start + '_" x="');
	abc.out_sxsy(x, '" y="', y);
	abc.out_svg('" width="' + w.toFixed(2) +
		'" height="' + abc.sh(h).toFixed(2) + '"/>\n')
}

	playconf.onnote = function(i, on) {
	    var	b, i, e, elts,
		x = 0,
		y = 0

		if (abc2svg.mu)			// if many tunes with same offsets
			elts = abc2svg.mu.d.getElementsByClassName('_' + i + '_')
		else
			elts = document.getElementsByClassName('_' + i + '_')







|







35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
	abc.out_svg('<rect class="abcr _' + start + '_" x="');
	abc.out_sxsy(x, '" y="', y);
	abc.out_svg('" width="' + w.toFixed(2) +
		'" height="' + abc.sh(h).toFixed(2) + '"/>\n')
}

	playconf.onnote = function(i, on) {
	    var	b, e, elts,
		x = 0,
		y = 0

		if (abc2svg.mu)			// if many tunes with same offsets
			elts = abc2svg.mu.d.getElementsByClassName('_' + i + '_')
		else
			elts = document.getElementsByClassName('_' + i + '_')
︙︙
Changes to util/play.js.
1
2
3
4
5
6
7
8
9
10
// snd-1.js - file to include in html pages with abc2svg-1.js for playing
//
// Copyright (C) 2015-2021 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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.


|







1
2
3
4
5
6
7
8
9
10
// snd-1.js - file to include in html pages with abc2svg-1.js for playing
//
// Copyright (C) 2015-2024 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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
︙︙
77
78
79
80
81
82
83
84

85
86
87
88
89
90
91
92
93
94
95
96
97
98






99
100
101
102
103
104
105
106
107
108
109
110
111
112
113





114
115
116
117
118
119
120
			midi5.get_outputs(play2) // get the MIDI ports
		else
			play2()
	} // play()

	// if set, out contains an array of the MIDI output ports
	function play2(out) {
	    var o


		if (!out)
			out = []
		o = audio5.get_outputs()	// get the HTML5 audio port
		if (o)
			Array.prototype.push.apply(out, o)
		if (out.length == 0) {
			if (conf.onend)		// no output port
				conf.onend()
			return
		}
		if (out.length == 1) {
			o = 0			// only one port
		} else {






			o = -1			// ask which port?
			var pr = "Use"
			for (var i = 0; i < out.length; i++)
				pr += "\n " + i + ": " + out[i]
			var res = window.prompt(pr, '0')
			if (res) {
				o = Number(res)
				if (isNaN(o) || o < 0 || o >= out.length)
					o = -1
			}
			if (!res || o < 0) {
				if (conf.onend)
					conf.onend()
				return
			}





		}

		// set the current output changing the play functions
		current = out[o] == 'sf2' ? audio5 : midi5;
		abcplay.play = current.play;
		abcplay.stop = current.stop
		if (current.set_output)







|
>














>
>
>
>
>
>















>
>
>
>
>







77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
			midi5.get_outputs(play2) // get the MIDI ports
		else
			play2()
	} // play()

	// if set, out contains an array of the MIDI output ports
	function play2(out) {
	    var o,
		n = window.sessionStorage && sessionStorage.getItem("audio")

		if (!out)
			out = []
		o = audio5.get_outputs()	// get the HTML5 audio port
		if (o)
			Array.prototype.push.apply(out, o)
		if (out.length == 0) {
			if (conf.onend)		// no output port
				conf.onend()
			return
		}
		if (out.length == 1) {
			o = 0			// only one port
		} else {
			if (n) {		// if port selected in a previous page
				for (o = 0; o < out.length; o++)
					if (out[o] == n)
						break
			}
		    if (!n || o >= out.length) {
			o = -1			// ask which port?
			var pr = "Use"
			for (var i = 0; i < out.length; i++)
				pr += "\n " + i + ": " + out[i]
			var res = window.prompt(pr, '0')
			if (res) {
				o = Number(res)
				if (isNaN(o) || o < 0 || o >= out.length)
					o = -1
			}
			if (!res || o < 0) {
				if (conf.onend)
					conf.onend()
				return
			}

			// memorize the output port for the next pages of the same session
			if (window.sessionStorage)
				sessionStorage.setItem("audio", out[o])
		    }
		}

		// set the current output changing the play functions
		current = out[o] == 'sf2' ? audio5 : midi5;
		abcplay.play = current.play;
		abcplay.stop = current.stop
		if (current.set_output)
︙︙
Changes to util/sndaud.js.
1
2
3
4
5
6
7
8
9
10
// sndaud.js - audio output using HTML5 audio
//
// Copyright (C) 2019-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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.


|







1
2
3
4
5
6
7
8
9
10
// sndaud.js - audio output using HTML5 audio
//
// Copyright (C) 2019-2024 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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
︙︙
128
129
130
131
132
133
134

135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164







165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214

215
216
217
218
219
220


221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260

261
262
263
264
265
266
267

	// create all notes of an instrument
	function sf2_create(instr, sf2par, sf2pre) {	// parser, presets

		// get the instrument parameters
		// adapted from getInstruments() in sf2-parser.js
		function get_instr(i) {

		    var	instrument = sf2par.instrument,
			zone = sf2par.instrumentZone,
			j = instrument[i].instrumentBagIndex,
			jl = instrument[i + 1]
				? instrument[i + 1].instrumentBagIndex
				: zone.length,
			info = []

			while (j < jl) {
				instrumentGenerator =
					sf2par.createInstrumentGenerator_(zone, j)
//				instrumentModulator =
//					sf2par.createInstrumentModulator_(zone, j)

				info.push({
					generator: instrumentGenerator.generator,
//					modulator: instrumentModulator.modulator
				})
				j++
			}
//console.log('created instr: '+instrument[i].instrumentName)
		return {
//			name: instrument[i].instrumentName,
			info: info
		}
	} // get_instr()

	// sf2_create
	    var i, j, k, sid, gen, parm, gparm, sample, infos,
		sampleRate, scale,







		b = instr >> 7,			// bank
		p = instr % 128,		// preset
		pr = sf2pre

		rates[instr] = []

		// search the bank:preset
		for (i = 0; i < pr.length; i++) {
			gen = pr[i].header
			if (gen.preset == p
			 && gen.bank == b)
				break
		}
		pr = pr[i]
		if (!pr) {
			errmsg('unknown instrument ' + b + ':' + p)
			return			// unknown preset!
		}
		pr = pr.info			// list of gen/mod
		for (k = 0; k < pr.length; k++) {
		    if (!pr[k].generator.instrument)



			continue
		    gparm = null

		    infos = get_instr(pr[k].generator.instrument.amount).info
		    for (i = 0; i < infos.length; i++) {
			gen = infos[i].generator

			if (!gparm) {
				parm = gparm = {	// default parameters
					attack: .001,
					hold: .001,
					decay: .001,
					sustain: 0
//					release: .001
				    }
			} else {
				parm = Object.create(gparm) // new parameters
				if (!gen.sampleID)
					gparm = parm	// global para,eters
			}

			if (gen.attackVolEnv)
				parm.attack = Math.pow(2,
						gen.attackVolEnv.amount / 1200)
			if (gen.holdVolEnv)
				parm.hold = Math.pow(2,
						gen.holdVolEnv.amount / 1200)
			if (gen.decayVolEnv)
				parm.decay = Math.pow(2,

						gen.decayVolEnv.amount / 1200) / 3
			if (gen.sustainVolEnv)
				parm.sustain = gen.sustainVolEnv.amount / 1000
//			if (gen.releaseVolEnv)
//				parm.release = Math.pow(2,
//						gen.releaseVolEnv.amount / 1200)


			if (gen.sampleModes && gen.sampleModes.amount & 1)
				parm.sm = 1

			if (!gen.sampleID)	// (empty generator!)
				continue

			sid = gen.sampleID.amount
			sampleRate = sf2par.sampleHeader[sid].sampleRate
			sample = sf2par.sample[sid]
			parm.buffer = ac.createBuffer(1,
						sample.length,
						sampleRate)

			parm.hold += parm.attack
			parm.decay += parm.hold

			// sustain > 40dB is not audible
			if (parm.sustain >= .4)
				parm.sustain = 0.01	// must not be null
			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
			}

			// define the notes
			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
			}
		    }







>
|








|





|












|
|
>
>
>
>
>
>
>




















|
>
>
>

<

|



|
|
<
<
<
|
<
<
<
|
<
<
<









>
|

|



>
>



|
<
<

|



|
<
<
<
<
<
<
<
<
<




|
|
|
|







|


|
>







128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197

198
199
200
201
202
203
204



205



206



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228


229
230
231
232
233
234









235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261

	// create all notes of an instrument
	function sf2_create(instr, sf2par, sf2pre) {	// parser, presets

		// get the instrument parameters
		// adapted from getInstruments() in sf2-parser.js
		function get_instr(i) {
		    var	generator,
			instrument = sf2par.instrument,
			zone = sf2par.instrumentZone,
			j = instrument[i].instrumentBagIndex,
			jl = instrument[i + 1]
				? instrument[i + 1].instrumentBagIndex
				: zone.length,
			info = []

			while (j < jl) {
				generator =
					sf2par.createInstrumentGenerator_(zone, j)
//				instrumentModulator =
//					sf2par.createInstrumentModulator_(zone, j)

				info.push({
					generator: generator.generator,
//					modulator: instrumentModulator.modulator
				})
				j++
			}
//console.log('created instr: '+instrument[i].instrumentName)
		return {
//			name: instrument[i].instrumentName,
			info: info
		}
	} // get_instr()

	// sf2_create
	    var i, j, k, sid, gen, parm, sample, infos,
		sample_hdr, scale, tune,
		gparm = {			// default parameters
			attack: .01,
			hold: .01,
			decay: .01,
//			sustain: 0,
//			release: .01,
		},
		b = instr >> 7,			// bank
		p = instr % 128,		// preset
		pr = sf2pre

		rates[instr] = []

		// search the bank:preset
		for (i = 0; i < pr.length; i++) {
			gen = pr[i].header
			if (gen.preset == p
			 && gen.bank == b)
				break
		}
		pr = pr[i]
		if (!pr) {
			errmsg('unknown instrument ' + b + ':' + p)
			return			// unknown preset!
		}
		pr = pr.info			// list of gen/mod
		for (k = 0; k < pr.length; k++) {
		    gen = pr[k].generator
//fixme: there may be generator definitions here
//fixme: what when many instruments?
		    if (!gen.instrument)
			continue


		    infos = get_instr(gen.instrument.amount).info
		    for (i = 0; i < infos.length; i++) {
			gen = infos[i].generator

			// check if already a generator for this key range
			j = gen.keyRange.lo



			parm = params[instr][j]



			parm =  Object.create(parm || gparm)




			if (gen.attackVolEnv)
				parm.attack = Math.pow(2,
						gen.attackVolEnv.amount / 1200)
			if (gen.holdVolEnv)
				parm.hold = Math.pow(2,
						gen.holdVolEnv.amount / 1200)
			if (gen.decayVolEnv)
				parm.decay = Math.pow(2,
//						gen.decayVolEnv.amount / 1200) / 3
						gen.decayVolEnv.amount / 1200) / 4
			if (gen.sustainVolEnv)
				parm.sustain = gen.sustainVolEnv.amount / 10	// in dB
//			if (gen.releaseVolEnv)
//				parm.release = Math.pow(2,
//						gen.releaseVolEnv.amount / 1200)
			if (gen.initialAttenuation)
				parm.atte = gen.initialAttenuation / 10		// in dB
			if (gen.sampleModes && gen.sampleModes.amount & 1)
				parm.sm = 1

		    if (gen.sampleID) {


			sid = gen.sampleID.amount
			sample_hdr = sf2par.sampleHeader[sid]
			sample = sf2par.sample[sid]
			parm.buffer = ac.createBuffer(1,
						sample.length,
						sample_hdr.sampleRate)










			sample_cp(parm.buffer, sample)

			if (parm.sm) {
				parm.loopStart = sample_hdr.startLoop /
					sample_hdr.sampleRate
				parm.loopEnd = sample_hdr.endLoop /
					sample_hdr.sampleRate
			}

			// define the notes
			scale = (gen.scaleTuning ?
					gen.scaleTuning.amount : 100) / 100,
			tune = (gen.coarseTune ? gen.coarseTune.amount : 0) +
				(gen.fineTune ? gen.fineTune.amount : 0) / 100 +
				sample_hdr.pitchCorrection / 100 -
				(gen.overridingRootKey ?
					gen.overridingRootKey.amount :
					sample_hdr.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
			}
		    }
︙︙
439
440
441
442
443
444
445




446
447
448
449
450
451
452
				po.v_b[s.v] = 0
			po.v_b[s.v] = (po.v_b[s.v] & ~0x1fc000)
					+ (s.val << 14)
			break
		case 7:				// volume
			s.p_v.vol = s.val / 127
			break




		case 32:			// bank LSB
			if (po.v_b[s.v] == undefined)
				po.v_b[s.v] = 0
			po.v_b[s.v] = (po.v_b[s.v] & ~0x3f80)
					+ (s.val << 7)
			break
		}







>
>
>
>







433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
				po.v_b[s.v] = 0
			po.v_b[s.v] = (po.v_b[s.v] & ~0x1fc000)
					+ (s.val << 14)
			break
		case 7:				// volume
			s.p_v.vol = s.val / 127
			break
		case 10:			// pan
			s.p_v.pan = s.val / 63.5 - 1
					// ((0) -1: left, (127) 1:right, (64) 0)
			break
		case 32:			// bank LSB
			if (po.v_b[s.v] == undefined)
				po.v_b[s.v] = 0
			po.v_b[s.v] = (po.v_b[s.v] & ~0x3f80)
					+ (s.val << 7)
			break
		}
︙︙
473
474
475
476
477
478
479

480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504



505
506
507
508
509
510
511

512
513

514
515
516




517

518



519



520

521
522
523
524
525
526
527
	// @s = symbol
	// @key = MIDI key + detune
	// @t = audio start time
	// @d = duration adjusted for speed
	function note_run(po, s, key, t, d) {
//console.log('run c:'+po.v_c[s.v]+' i:'+po.c_i[po.v_c[s.v]])
	    var	g, st,

		c = po.v_c[s.v],
		instr = po.c_i[c],
		k = key | 0,
		parm = params[instr][k],
		o = po.ac.createBufferSource(),
		v = s.p_v.vol == undefined ? 1 : s.p_v.vol	// volume (gain)

		if (!v			// mute voice
		 || !parm)		// if the instrument could not be loaded
			return		// or if it has not this key
		o.buffer = parm.buffer
		if (parm.loopStart) {
			o.loop = true
			o.loopStart = parm.loopStart
			o.loopEnd = parm.loopEnd
		}
		if (o.detune) {
		    var	dt = (key * 100) % 100
			if (dt)			// if micro-tone
				 o.detune.value = dt
		}
//		o.playbackRate.setValueAtTime(parm.rate, ac.currentTime)
		o.playbackRate.value = po.rates[instr][k]

		g = po.ac.createGain()



		if (parm.hold < 0.002) {
			g.gain.setValueAtTime(v, t)
		} else {
			if (parm.attack < 0.002) {
				g.gain.setValueAtTime(v, t)
			} else {
				g.gain.setValueAtTime(0, t)

				g.gain.linearRampToValueAtTime(v, t + parm.attack)
			}

			g.gain.setValueAtTime(v, t + parm.hold)
		}





		g.gain.exponentialRampToValueAtTime(parm.sustain * v,

					t + parm.decay)







		o.connect(g)

		g.connect(po.gain)

		// start the note
		o.start(t)
		o.stop(t + d)
	} // note_run()








>











|













>
>
>
|


|


|
>
|

>
|


>
>
>
>
|
>
|
>
>
>
|
>
>
>
|
>







471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
	// @s = symbol
	// @key = MIDI key + detune
	// @t = audio start time
	// @d = duration adjusted for speed
	function note_run(po, s, key, t, d) {
//console.log('run c:'+po.v_c[s.v]+' i:'+po.c_i[po.v_c[s.v]])
	    var	g, st,
		t2 = t,
		c = po.v_c[s.v],
		instr = po.c_i[c],
		k = key | 0,
		parm = params[instr][k],
		o = po.ac.createBufferSource(),
		v = s.p_v.vol == undefined ? 1 : s.p_v.vol	// volume (gain)

		if (!v			// mute voice
		 || !parm)		// if the instrument could not be loaded
			return		// or if it has not this key
		o.buffer = parm.buffer
		if (parm.sm) {
			o.loop = true
			o.loopStart = parm.loopStart
			o.loopEnd = parm.loopEnd
		}
		if (o.detune) {
		    var	dt = (key * 100) % 100
			if (dt)			// if micro-tone
				 o.detune.value = dt
		}
//		o.playbackRate.setValueAtTime(parm.rate, ac.currentTime)
		o.playbackRate.value = po.rates[instr][k]

		g = po.ac.createGain()

		if (parm.atte)			// if initial attenuation
			v /= Math.pow(10, parm.atte / 20)
		if (parm.hold <= .01) {
			g.gain.setValueAtTime(v, t)
		} else {
			if (parm.attack <= .01) {
				g.gain.setValueAtTime(v, t)
			} else {
				g.gain.setValueAtTime(.01, t)
				t2 += parm.attack
				g.gain.linearRampToValueAtTime(v, t2)
			}
			t2 += parm.hold
			g.gain.setValueAtTime(v, t2)
		}

		if (parm.sustain && parm.decay > .01) {
			v = parm.sustain == 100
				? .01			// 100dB -> full attenuation
				: v / Math.pow(10, parm.sustain / 20)
			g.gain.exponentialRampToValueAtTime(v, t2 + parm.decay)
		}

		if (s.p_v.pan != undefined) {	// (control 10)
		    var	p = po.ac.createStereoPanner()
			p.pan.value = s.p_v.pan
					
			o.connect(p)
			p.connect(g)
		} else {
			o.connect(g)
		}
		g.connect(po.gain)

		// start the note
		o.start(t)
		o.stop(t + d)
	} // note_run()

︙︙
Changes to util/sndgen.js.
︙︙
27
28
29
30
31
32
33















34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// - in NOTE and REST
//	s.pdur = play duration
// - in the notes[] of NOTE
//	s.notes[i].midi

if (!abc2svg)
    var	abc2svg = {}
















function ToAudio() {
 return {

   // generate the play data of a tune
   add: function(first,		// starting symbol
		voice_tb,	// voice table
		cfmt) {		// tune parameters
    var	toaud = this,
	C = abc2svg.C,
	p_time = 0,		// last playing time
	abc_time = 0,		// last ABC time
	play_fac = C.BLEN / 4 * 120 / 60, // play time factor - default: Q:1/4=120
	i, n, dt, d, v,
	s = first,
	rst = s,		// left repeat (repeat restart)
	rst_fac,		// play factor on repeat restart
	rsk = [],		// repeat variant array (repeat skip)
	b_tim,			// time of last measure bar
	b_typ			// type of last measure bar








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>













|







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// - in NOTE and REST
//	s.pdur = play duration
// - in the notes[] of NOTE
//	s.notes[i].midi

if (!abc2svg)
    var	abc2svg = {}

// tempo table
	abc2svg.tmp_tb = {
		grave:		30,
		lento:		40,
		largo:		50,
		adagio:		66,
		andante:	86,
		moderato:	96,
		allegretto:	112,
		allegro:	130,
		vivace:		150,
		presto:		180,
		prestissimo:	210
	}

function ToAudio() {
 return {

   // generate the play data of a tune
   add: function(first,		// starting symbol
		voice_tb,	// voice table
		cfmt) {		// tune parameters
    var	toaud = this,
	C = abc2svg.C,
	p_time = 0,		// last playing time
	abc_time = 0,		// last ABC time
	play_fac = C.BLEN / 4 * 120 / 60, // play time factor - default: Q:1/4=120
	i, n, dt, d, v, s_m,
	s = first,
	rst = s,		// left repeat (repeat restart)
	rst_fac,		// play factor on repeat restart
	rsk = [],		// repeat variant array (repeat skip)
	b_tim,			// time of last measure bar
	b_typ			// type of last measure bar

︙︙
215
216
217
218
219
220
221























222
223
224
225
226
227
228
				for (i = 0; i < first.parts.length; i++) {
					if (first.parts[i] == v)
						first.p_s[i] = s
				}
			}
		}
	} // build_parts()
























	// generate the grace notes
	function gen_grace(s) {
	    var	g, i, n, t, d, s2,
		next = s.next

		// before beat







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
				for (i = 0; i < first.parts.length; i++) {
					if (first.parts[i] == v)
						first.p_s[i] = s
				}
			}
		}
	} // build_parts()

	// update the time linkage when the start time has changed
	function relink(s, dt) {
	    var	tim = s.time + dt,		// new time
		s2 = s.ts_next

		s.ts_prev.ts_next = s2		// remove from the time linkage
		s2.ts_prev = s.ts_prev

		while (!s2.seqst && s2.ts_next)
			s2 = s2.ts_next
		if (s2.time < s.time + dt)	// don't move after the next time seq.
			dt = s2.time - s.time
		s.time += dt			// update time and duration
		s.dur -= dt
		s.ts_prev = s2.ts_prev		// update the time linkage
		s.ts_prev.ts_next = s
		s.ts_next = s2
		s2.ts_prev = s
		s.seqst = 1 //true
		if (s2.time == s.time)
			s2.seqst = 0 //false
	} // relink()

	// generate the grace notes
	function gen_grace(s) {
	    var	g, i, n, t, d, s2,
		next = s.next

		// before beat
︙︙
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261



















262
263
264
265
266
267


268
269
270
271
272
273
274
			d = next.dur / 12
			if (!(d & (d - 1)))
				d = next.dur / 2	// no dot
			else
				d = next.dur / 3
			if (s.p_v.key.k_bagpipe)
				d /= 2
			next.time += d
			next.dur -= d
		}
//fixme: assume the grace notes in the sequence have the same duration
		n = 0
		for (g = s.extra; g; g = g.next)
			n++
		d /= n * play_fac
		t = p_time
		for (g = s.extra; g; g = g.next) {
			g.ptim = t
			g.pdur = d
			t += d
		}
	} // gen_grace()

	// change the tempo
	function set_tempo(s) {



















	    var	i,
		d = 0,
		n = s.tempo_notes.length

		for (i = 0; i < n; i++)
			d += s.tempo_notes[i]


		return d * s.tempo / 60
	} // set_tempo()

	function set_variant(s) {
	    var	d,
		n = s.text.match(/[1-8]-[2-9]|[1-9,.]|[^\s]+$/g)








|
<
















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






>
>







275
276
277
278
279
280
281
282

283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
			d = next.dur / 12
			if (!(d & (d - 1)))
				d = next.dur / 2	// no dot
			else
				d = next.dur / 3
			if (s.p_v.key.k_bagpipe)
				d /= 2
			relink(next, d)

		}
//fixme: assume the grace notes in the sequence have the same duration
		n = 0
		for (g = s.extra; g; g = g.next)
			n++
		d /= n * play_fac
		t = p_time
		for (g = s.extra; g; g = g.next) {
			g.ptim = t
			g.pdur = d
			t += d
		}
	} // gen_grace()

	// change the tempo
	function set_tempo(s) {
		if (!s.tempo && !s.new_beat) {
		    var	p = (s.tempo_str1 || s.tempo_str2)
			if (!p)
				return play_fac		// no change
		    var	upm = abc2svg.tmp_tb[p.toLowerCase()]

			if (!upm)
				return play_fac		// no change
			upm *= C.BLEN / 240 // (/4/60)
			if (!s_m.a_meter)
				return upm
			if (!s_m.a_meter[0].bot)
				return (s_m.a_meter[1] && s_m.a_meter[1].top == '|')
						? upm * 2 : upm
			if (s_m.a_meter[0].bot == "8"
			 && !(s_m.a_meter[0].top % 3))
			 	return upm / 2 * 3
			return upm * s_m.a_meter[0].bot / 4
		}
	    var	i,
		d = 0,
		n = s.tempo_notes.length

		for (i = 0; i < n; i++)
			d += s.tempo_notes[i]
		if (s.new_beat)
			return play_fac * s.new_beat / d
		return d * s.tempo / 60
	} // set_tempo()

	function set_variant(s) {
	    var	d,
		n = s.text.match(/[1-8]-[2-9]|[1-9,.]|[^\s]+$/g)

︙︙
289
290
291
292
293
294
295

296

297
298
299
300
301
302
303
	// add() main

	// if some chord stuff, set the accompaniment data
	if (cfmt.chord)
		abc2svg.chord(first, voice_tb, cfmt)

	// if %%playbeats, create the sounds

	if (cfmt.playbeats)

		def_beats()

	if (s.parts)
		build_parts(s)

	// set the time parameters
	rst_fac = play_fac







>
|
>







347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
	// add() main

	// if some chord stuff, set the accompaniment data
	if (cfmt.chord)
		abc2svg.chord(first, voice_tb, cfmt)

	// if %%playbeats, create the sounds
	s_m = first.p_v.meter			// current meter
	if (cfmt.playbeats
	  && first.p_v.meter.wmeasure != 1)	// if not M:none
		def_beats()

	if (s.parts)
		build_parts(s)

	// set the time parameters
	rst_fac = play_fac
︙︙
339
340
341
342
343
344
345

346
347
348
349
350
351
352
			if (s.bar_type[0] == ':') {
				if (b_typ & 2)
					break
				b_typ |= 2
				s.rep_p = rst		// :| to |:
				if (rst == rsk[0])
					s.rep_v = rsk	// to know the number of variants

			}

			// 1st time repeat
			if (s.text) {
			    if (s.text[0] == '1') {
				if (b_typ & 1)
					break







>







399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
			if (s.bar_type[0] == ':') {
				if (b_typ & 2)
					break
				b_typ |= 2
				s.rep_p = rst		// :| to |:
				if (rst == rsk[0])
					s.rep_v = rsk	// to know the number of variants
				rst = s			// possible restart (..:|..:|)
			}

			// 1st time repeat
			if (s.text) {
			    if (s.text[0] == '1') {
				if (b_typ & 1)
					break
︙︙
400
401
402
403
404
405
406



407
408
409
410
411
412
413
414
415
416
				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
	} // loop
   } // add()
 } // return
} // ToAudio()







>
>
>

<
|







461
462
463
464
465
466
467
468
469
470
471

472
473
474
475
476
477
478
479
				s.next.time -= dt
				d -= dt
			}
			d /= play_fac
			s.pdur = d
			v = s.v
			break
		case C.METER:
			s_m = s				// current meter
			break
		case C.TEMPO:

			play_fac = set_tempo(s)
			break
		}
		s = s.ts_next
	} // loop
   } // add()
 } // return
} // ToAudio()
︙︙
503
504
505
506
507
508
509


510
511
512
513
514
515
516
517
518
519



520

521
522
523
524
525
526
527
528



529
530
531
532
533
534
535
536
537
538
539
540
541
542
	    var	i,
		p_v = s2.p_v,
		s = {
			subtype: "midictl",
			p_v: p_v,
			v: s2.v
		}



		for (i in p_v.midictl) { // MIDI controls at voice start time
			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)

		}

		// if no %%MIDI, set 'grand acoustic piano' as the instrument
		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	// piano




		po.p_v[s2.v] = true	// synchronization done
	} // set_ctrl()

    // start and continue to play
    function play_cont(po) {
    var	d, i, st, m, note, g, s2, t, maxt, now,
	C = abc2svg.C,
	s = po.s_cur

	// search the end of a sequence of variants
	function var_end(s) {
	    var	i, s2, s3,
		a = s.rep_v || s.rep_s







>
>







|

|
>
>
>

>








>
>
>
|





|







566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
	    var	i,
		p_v = s2.p_v,
		s = {
			subtype: "midictl",
			p_v: p_v,
			v: s2.v
		}

		p_v.vol = p_v.pan = undefined	// reset the controllers

		for (i in p_v.midictl) { // MIDI controls at voice start time
			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.v_c[s.v] = s.chn
				if (s.instr != undefined)
					po.c_i[po.v_c[s.v]] = s.instr
				po.midi_prog(po, s)
			}
		}

		// if no %%MIDI, set 'grand acoustic piano' as the instrument
		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	// piano
		while (p_v.voice_down) {
			p_v = p_v.voice_down
			po.v_c[p_v.v] = i
		}
		po.p_v[s2.v] = true	// synchronization done
	} // set_ctrl()

    // start and continue to play
    function play_cont(po) {
    var	d, i, st, m, note, g, s2, t, maxt, now, p_v,
	C = abc2svg.C,
	s = po.s_cur

	// search the end of a sequence of variants
	function var_end(s) {
	    var	i, s2, s3,
		a = s.rep_v || s.rep_s
︙︙
600
601
602
603
604
605
606


607
608
609
610
611
612
613
				  || po.repv <= s.rep_v.length)) {
					s2 = s.rep_p	// left repeat
					po.repn = true
				} else {
					if (s.rep_v)
						s2 = var_end(s)
					po.repn = false


				}
			}
			if (s.rep_s) {			// first variant
				s2 = s.rep_s[po.repv]	// next variant
				if (s2) {
					po.repn = false
					if (s2 == s)







>
>







672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
				  || po.repv <= s.rep_v.length)) {
					s2 = s.rep_p	// left repeat
					po.repn = true
				} else {
					if (s.rep_v)
						s2 = var_end(s)
					po.repn = false
					if (s.bar_type.slice(-1) == ':') // if ::
						po.repv = 1
				}
			}
			if (s.rep_s) {			// first variant
				s2 = s.rep_s[po.repv]	// next variant
				if (s2) {
					po.repn = false
					if (s2 == s)
︙︙
659
660
661
662
663
664
665
666
667
668



669






670
671
672
673
674
675
676
			break
		}
	    if (s && s != po.s_end && !s.noplay) {
		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)







|

|
>
>
>

>
>
>
>
>
>







733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
			break
		}
	    if (s && s != po.s_end && !s.noplay) {
		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.v_c[s.v] = s.chn
				if (s.instr != undefined)
					po.c_i[po.v_c[s.v]] = s.instr
				po.midi_prog(po, s)
				p_v = s.p_v
				while (p_v.voice_down) {
					p_v = p_v.voice_down
					po.v_c[p_v.v] = s.chn
				}
			}
			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)
︙︙
Changes to util/sndmid.js.
1
2
3
4
5
6
7
8
9
10
// sndmid.js - audio output using HTML5 MIDI
//
// Copyright (C) 2019-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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.


|







1
2
3
4
5
6
7
8
9
10
// sndmid.js - audio output using HTML5 MIDI
//
// Copyright (C) 2019-2024 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 Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
︙︙
45
46
47
48
49
50
51

52
53
54
55
56
57
58

































59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//
// stop() - stop playing

function Midi5(i_conf) {
    var	po,
	conf = i_conf,		// configuration
	empty = function() {},

	rf,			// get_outputs result function
	op			// output MIDI port

	// return the play real time in seconds
	function get_time(po) {
		return window.performance.now() / 1000
	} // get_time()


































	// create a note
	// @po = play object
	// @s = symbol
	// @k = MIDI key + detune
	// @t = audio start time (s)
	// @d = duration adjusted for speed (s)
	function note_run(po, s, k, t, d) {
	    var	j,
		a = (k * 100) % 100,	// detune in cents
		c = po.v_c[s.v],
		i = po.c_i[c]

		k |= 0			// remove the detune value

		t *= 1000		// convert to ms
		d *= 1000		

		if (a && Midi5.ma.sysexEnabled) {	// if microtone
// fixme: should cache the current microtone values
			po.op.send(new Uint8Array([
				0xf0, 0x7f,	// realtime SysEx
				0x7f,		// all devices
				0x08,		// MIDI tuning standard
				0x02,		// note change
				i & 0x7f,		// tuning prog number
				0x01,		// number of notes
					k,		// key
					k,		// note
					a / .78125,	// MSB fract
					0,		// LSB fract
				0xf7		// SysEx end
				]), t)
		}
		po.op.send(new Uint8Array([0x90 + c, k, 127]), t)	// note on
		po.op.send(new Uint8Array([0x80 + c, k, 0]), t + d - 20) // note off
	} // note_run()

	// send a MIDI control
	function midi_ctrl(po, s, t) {
		po.op.send(new Uint8Array([0xb0 + po.v_c[s.v],







>







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
















|

<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
|







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

111













112
113
114
115
116
117
118
119
//
// stop() - stop playing

function Midi5(i_conf) {
    var	po,
	conf = i_conf,		// configuration
	empty = function() {},
	det_tb,			// detune table
	rf,			// get_outputs result function
	op			// output MIDI port

	// return the play real time in seconds
	function get_time(po) {
		return window.performance.now() / 1000
	} // get_time()

    var	mutone = function(k, a, t) {
		if (!det_tb) {
			if (!a)
				return		// no microtone
			det_tb = {}
		}
		if (!Midi5.ma.sysexEnabled) {	// no SysEx
			mutone = empty		// don't come back here anymore
			return
		}
	    var	old_a = det_tb[k]		// previous detune of 'k'

		if ((!old_a && !a)
		 || old_a == a)
			return			// same detune
		det_tb[k] = a

		a *= 163.84			// 16384 / 100
		po.op.send(new Uint8Array([
			0xf0, 0x7f,		// realtime SysEx
			0x7f,			// all devices
			0x08,			// MIDI tuning
			0x02,			// note change
			0,			// tuning prog number
			0x01,			// number of notes
				k,		// key
				k,		// semitone
				(a >> 7) & 0x7f, // fraction of semitone
				a & 0x7f,
			0xf7			// SysEx end
			]), t)
	} // mutone()

	// create a note
	// @po = play object
	// @s = symbol
	// @k = MIDI key + detune
	// @t = audio start time (s)
	// @d = duration adjusted for speed (s)
	function note_run(po, s, k, t, d) {
	    var	j,
		a = (k * 100) % 100,	// detune in cents
		c = po.v_c[s.v],
		i = po.c_i[c]

		k |= 0			// remove the detune value

		t *= 1000		// convert to ms
		d *= 1000


		mutone(k, a, t)		// handle microtone accidentals














		po.op.send(new Uint8Array([0x90 + c, k, 127]), t)	// note on
		po.op.send(new Uint8Array([0x80 + c, k, 0]), t + d - 20) // note off
	} // note_run()

	// send a MIDI control
	function midi_ctrl(po, s, t) {
		po.op.send(new Uint8Array([0xb0 + po.v_c[s.v],
︙︙
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
			}
			rf = f

			// open MIDI with SysEx
			navigator.requestMIDIAccess({sysex: true}).then(
				send_outputs,
				function(msg) {

					// open MIDI without SysEx
					navigator.requestMIDIAccess().then(
						send_outputs,
						function(msg) {
							rf()
						}
					)
				}
			)
		}, // get_outputs()

		// set the output port
		set_output: function(name) {
			if (!Midi5.ma)







<
<
<
<
<
|
<
<







181
182
183
184
185
186
187





188


189
190
191
192
193
194
195
			}
			rf = f

			// open MIDI with SysEx
			navigator.requestMIDIAccess({sysex: true}).then(
				send_outputs,
				function(msg) {





					rf()


				}
			)
		}, // get_outputs()

		// set the output port
		set_output: function(name) {
			if (!Midi5.ma)
︙︙
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
				timouts: [],

				// MIDI specific
				op: op,		// output port
				v_c: [],	// voice to channel
				c_i: []		// channel to instrument
			}
if (0) {
// temperament
			op.send(new Uint8Array([
				0xf0, 0x7f,	// realtime SysEx
				0x7f,		// all devices
				0x08,		// MIDI tuning standard
				0x02,		// note change
				0x00,		// tuning prog number
				0x01,		// number of notes
					0x69,		// key
					0x69,		// note
					0x00,		// MSB fract
					0,		// LSB fract
				0xf7		// SysEx end
				]), t)
}

			abc2svg.play_next(po)
		}, // play()

		// stop playing
		stop: function() {
			po.stop = true







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







227
228
229
230
231
232
233
















234
235
236
237
238
239
240
				timouts: [],

				// MIDI specific
				op: op,		// output port
				v_c: [],	// voice to channel
				c_i: []		// channel to instrument
			}

















			abc2svg.play_next(po)
		}, // play()

		// stop playing
		stop: function() {
			po.stop = true
︙︙
Added version.txt.


>
1
abc2svg.version="v1.22.23";abc2svg.vdate="2024-12-14"