App-Music-ChordPro
view release on metacpan or search on metacpan
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
//
// 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/>.
var sav = {}, // save global (between tunes) definitions
mac = {}, // macros (m:)
maci = {}, // first letters of macros
modone = {} // hooks done by module
// translation table from the ABC draft version 2.2
var abc_utf = {
"=D": "Ä",
"=H": "Ħ",
"=T": "Ŧ",
"=d": "Ä",
"=h": "ħ",
"=t": "ŧ",
"/O": "Ã",
"/o": "ø",
// "/D": "Ä",
// "/d": "Ä",
"/L": "Å",
"/l": "Å",
"vL": "Ľ",
"vl": "ľ",
"vd": "Ä",
".i": "ı",
"AA": "Ã
",
"aa": "Ã¥",
"AE": "Ã",
"ae": "æ",
"DH": "Ã",
"dh": "ð",
// "ng": "Å",
"OE": "Å",
"oe": "Å",
"ss": "Ã",
"TH": "Ã",
"th": "þ"
}
// accidentals as octal values (abcm2ps compatibility)
var oct_acc = {
"1": "\u266f",
"2": "\u266d",
"3": "\u266e",
"4": "𝄪",
"5": "𝄫"
}
// convert the escape sequences to utf-8
function cnv_escape(src, flag) {
var c, c2,
dst = "",
i, j = 0
while (1) {
i = src.indexOf('\\', j)
if (i < 0)
break
dst += src.slice(j, i);
c = src[++i]
if (!c)
return dst + '\\'
switch (c) {
case '0':
case '2':
if (src[i + 1] != '0')
break
c2 = oct_acc[src[i + 2]]
if (c2) {
dst += c2;
j = i + 3
continue
}
break
case 'u':
j = Number("0x" + src.slice(i + 1, i + 5));
if (isNaN(j) || j < 0x20) {
dst += src[++i] + "\u0306" // breve accent
j = i + 1
continue
}
c = String.fromCharCode(j)
if (c == '\\') {
i += 4
break
}
dst += c
j = i + 5
continue
case 't': // TAB
dst += '\t';
j = i + 1
continue
case 'n': // new line (voice name)
dst += '\n';
j = i + 1
continue
default:
c2 = abc_utf[src.slice(i, i + 2)]
if (c2) {
dst += c2;
j = i + 2
continue
}
// try unicode combine characters
c2 = src[i + 1]
if (!c2)
break // !! the next test is true if c2 is undefined !!
if (!/[A-Za-z]/.test(c2))
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
function do_include(fn) {
var file, parse_sav
if (!user.read_file) {
syntax(1, "No read_file support")
return
}
if (include > 2) {
syntax(1, "Too many include levels")
return
}
file = user.read_file(fn)
if (!file) {
syntax(1, "Cannot read file '$1'", fn)
return
}
include++;
parse_sav = clone(parse);
tosvg(fn, file);
parse_sav.state = parse.state;
parse_sav.ckey = parse.ckey
parse = parse_sav;
include--
}
// parse ABC code
function tosvg(in_fname, // file name
file, // file content
bol, eof) { // beginning/end of file
var i, c, eol, end,
select,
line0, line1,
last_info, opt, text, a, b, s,
pscom,
txt_add = '\n' // for "+:"
// check if a tune is selected
function tune_selected() {
var re, res,
i = file.indexOf('K:', bol)
if (i < 0) {
// syntax(1, "No K: in tune")
return false
}
i = file.indexOf('\n', i)
if (parse.select.test(file.slice(parse.bol, i)))
return true
re = /\n\w*\n/;
re.lastIndex = i;
res = re.exec(file)
if (res)
eol = re.lastIndex
else
eol = eof
return false
} // tune_selected()
// remove the comment at end of text
// if flag, handle the escape sequences
// if flag is 'w' (lyrics), keep the '\'s
function uncomment(src, flag) {
if (!src)
return src
var i = src.indexOf('%')
if (i == 0)
return ''
if (i > 0)
src = src.replace(/([^\\])%.*/, '$1')
.replace(/\\%/g, '%');
src = src.replace(/\s+$/, '')
if (flag && src.indexOf('\\') >= 0)
return cnv_escape(src, flag)
return src
} // 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)
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
val += 7
break
case ',':
val -= 7
if (item[2] == ',')
val -= 7
break
}
clefpit = 4 - val // 4 = 'G'
break
}
}
syntax(1, errs.bad_val, item)
break
case "octave=":
val = +a.shift()
if (isNaN(val))
syntax(1, errs.bad_val, item)
else
curvoice.octave = val
break
case "cue=":
// (ignore cue=off)
// curvoice.scale = a.shift() == 'on' ? .7 : 1
if (a.shift() == 'on')
curvoice.scale = .7
break
case "instrument=":
// instrument=M/N => score=MN and sound=cN
// (instrument=M == instrument=M/M)
item = a.shift()
val = item.indexOf('/')
if (val < 0) {
val = get_interval('c' + item)
if (val == undefined)
break
curvoice.sound = val
tr_p |= 2
val = 0
} else {
val = get_interval('c' + item.slice(val + 1))
if (val == undefined)
break
curvoice.sound = val
tr_p |= 2
val = get_interval(item.replace('/', ''))
if (val == undefined)
break
}
curvoice.score = cfmt.sound ? curvoice.sound : val
tr_p |= 1
break
case "map=": // %%voicemap
curvoice.map = a.shift()
break
case "name=":
case "nm=":
curvoice.nm = a.shift()
if (curvoice.nm[0] == '"')
curvoice.nm = cnv_escape(curvoice.nm.slice(1, -1))
curvoice.new_name = true
break
case "stem=": // compatibility
case "pos=": // from %%pos only
if (item == "pos=")
item = a.shift()
.slice(1, -1) // always inside dble quotes
.split(' ')
else
item = ["stm", a.shift()];
val = posval[item[1]]
if (val == undefined) {
syntax(1, errs.bad_val, "%%pos")
break
}
switch (item[2]) {
case "align": val |= C.SL_ALIGN; break
case "center": val |= C.SL_CENTER; break
case "close": val |= C.SL_CLOSE; break
}
if (!pos)
pos = {}
pos[item[0]] = val
break
case "scale=": // %%voicescale
val = +a.shift()
if (isNaN(val) || val < .5 || val > 2)
syntax(1, errs.bad_val, "%%voicescale")
else
curvoice.scale = val
break
case "score=":
if (cfmt.nedo) {
syntax(1, errs.notransp)
break
}
// score=MN
// (score=M == score=Mc)
item = a.shift()
if (cfmt.sound)
break
val = get_interval(item, true)
if (val != undefined) {
curvoice.score = val
tr_p |= 1
}
break
case "shift=":
if (cfmt.nedo) {
syntax(1, errs.notransp)
break
}
val = get_interval(a.shift())
if (val != undefined) {
curvoice.shift = val
tr_p = 3
}
break
case "sound=":
if (cfmt.nedo) {
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
case "js":
js_inject(text)
break
case "ml":
if (cfmt.pageheight) {
syntax(1, "Cannot have %%beginml with %%pageheight")
break
}
if (parse.state >= 2) {
s = new_block(type);
s.text = text
} else {
blk_flush()
if (user.img_out)
user.img_out(text)
}
break
case "svg":
j = 0
while (1) {
i = text.indexOf('<style', j)
if (i < 0)
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)
if (j < 0) {
syntax(1, "No </defs> in %%beginsvg sequence")
break
}
defs_add(text.slice(i + 6, j))
}
break
case "text":
action = get_textopt(opt);
if (!action)
action = cfmt.textoption
set_font("text")
if (text.indexOf('\\') >= 0)
text = cnv_escape(text)
if (parse.state > 1) {
s = new_block(type);
s.text = text
s.opt = action
s.font = cfmt.textfont
break
}
write_text(text, action)
break
}
}
/* -- generate a piece of tune -- */
function generate() {
var s, v, p_voice;
if (a_dcn.length) {
syntax(1, "Decoration(s) without symbol: $1", a_dcn)
a_dcn = []
}
if (parse.tp) {
syntax(1, "No end of tuplet")
s = parse.tps
if (s)
delete s.tp
delete parse.tp
}
if (vover) {
syntax(1, "No end of voice overlay");
get_vover(vover.bar ? '|' : ')')
}
self.voice_adj()
sort_all() /* define the time / vertical sequences */
if (tsfirst) {
for (v = 0; v < voice_tb.length; v++) {
if (!voice_tb[v].key)
voice_tb[v].key = parse.ckey // set the starting key
}
if (user.anno_start)
anno_start = a_start
if (user.anno_stop)
anno_stop = a_stop
self.set_bar_num()
if (info.P)
tsfirst.parts = info.P // for play
// give the parser result to the application
if (user.get_abcmodel)
user.get_abcmodel(tsfirst, voice_tb, abc2svg.sym_name, info)
if (user.img_out) // if SVG generation
self.output_music()
} // (tsfirst)
// finish the generation
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
}
}
}
// abc2svg - gchord.js - chord symbols
//
// Copyright (C) 2014-2025 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/>.
// -- parse a chord symbol / annotation --
// the result is added in the global variable a_gch
// 'type' may be a single '"' or a string '"xxx"' created by U:
function parse_gchord(type) {
var c, text, gch, x_abs, y_abs,
i, j, istart, iend,
ann_font = get_font("annotation"),
h_ann = ann_font.size,
line = parse.line
function get_float() {
var txt = ''
while (1) {
c = text[i++]
if ("1234567890.-".indexOf(c) < 0)
return parseFloat(txt)
txt += c
}
} // get_float()
istart = parse.bol + line.index
if (type.length > 1) { // U:
text = type.slice(1, -1);
iend = istart + 1
} else {
i = ++line.index // search the ending double quote
while (1) {
j = line.buffer.indexOf('"', i)
if (j < 0) {
syntax(1, "No end of chord symbol/annotation")
return
}
if (line.buffer[j - 1] != '\\'
|| line.buffer[j - 2] == '\\') // (string ending with \\")
break
i = j + 1
}
text = cnv_escape(line.buffer.slice(line.index, j))
line.index = j
iend = parse.bol + line.index + 1
}
if (ann_font.pad)
h_ann += ann_font.pad
i = 0;
type = 'g'
while (1) {
c = text[i]
if (!c)
break
gch = {
text: "",
istart: istart,
iend: iend,
font: ann_font
}
switch (c) {
case '@':
type = c;
i++;
x_abs = get_float()
if (c != ',') {
syntax(1, "',' lacking in annotation '@x,y'");
y_abs = 0
} else {
y_abs = get_float()
if (c != ' ')
i--
}
gch.x = x_abs;
gch.y = y_abs
break
case '^':
gch.pos = C.SL_ABOVE
// fall thru
case '_':
if (c == '_')
gch.pos = C.SL_BELOW
// fall thru
case '<':
case '>':
i++;
type = c
break
default:
switch (type) {
case 'g':
gch.font = get_font("gchord")
gch.pos = curvoice.pos.gch || C.SL_ABOVE
break
case '^':
gch.pos = C.SL_ABOVE
break
case '_':
gch.pos = C.SL_BELOW
break
case '@':
gch.x = x_abs;
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
+ "FCGDAEB"[(n + 22) % 7]
+ (n >= 13 ? '##'
: n >= 6 ? '#'
: n <= -9 ? 'bb'
: n <= -2 ? 'b'
: '')
+ p.slice(ip)
}
return csa.join('/')
} // gch_tr1
// parser: add the parsed list of chord symbols and annotations
// to the symbol (note, rest or bar)
// and transpose the chord symbols
function csan_add(s) {
var i, gch
// there cannot be chord symbols on measure bars
if (s.type == C.BAR) {
for (i = 0; i < a_gch.length; i++) {
if (a_gch[i].type == 'g') {
error(1, s,
"There cannot be chord symbols on measure bars")
a_gch.splice(i)
}
}
}
if (curvoice.tr_sco
|| curvoice.tr_snd) {
for (i = 0; i < a_gch.length; i++) {
gch = a_gch[i]
if (gch.type == 'g') {
if (curvoice.tr_snd40)
gch.otext = gch_tr1(gch.text, curvoice.tr_snd40)
if (curvoice.tr_sco)
gch.text = gch_tr1(gch.text, curvoice.tr_sco)
}
}
}
if (s.a_gch)
s.a_gch = s.a_gch.concat(a_gch)
else
s.a_gch = a_gch
a_gch = null
} // csan_add
// generator: build the chord symbols / annotations
// (possible hook)
Abc.prototype.gch_build = function(s) {
/* split the chord symbols / annotations
* and initialize their vertical offsets */
var gch, wh, xspc, ix,
y_left = 0,
y_right = 0,
GCHPRE = .4; // portion of chord before note
// change the accidentals in the chord symbols,
// convert the escape sequences in annotations, and
// set the offsets
for (ix = 0; ix < s.a_gch.length; ix++) {
gch = s.a_gch[ix]
if (gch.type == 'g') {
gch.text = gch.text.replace(/##|#|=|bb|b/g,
function(x) {
switch (x) {
case '##': return "𝄪"
case '#': return "\u266f"
case '=': return "\u266e"
case 'b': return "\u266d"
}
return "𝄫"
});
} else {
if (gch.type == '@'
&& !user.anno_start && !user.anno_stop) {
set_font(gch.font)
gch.text = str2svg(gch.text)
continue /* no width */
}
}
/* set the offsets and widths */
set_font(gch.font);
gch.text = str2svg(gch.text)
wh = gch.text.wh
switch (gch.type) {
case '@':
break
default:
// case 'g': // chord symbol
// case '^': /* above */
// case '_': /* below */
xspc = wh[0] * GCHPRE
if (xspc > 8)
xspc = 8;
gch.x = -xspc;
break
case '<': /* left */
gch.x = -(wh[0] + 6);
y_left -= wh[1];
gch.y = y_left + wh[1] / 2
break
case '>': /* right */
gch.x = 6;
y_right -= wh[1];
gch.y = y_right + wh[1] / 2
break
}
}
/* move upwards the top and middle texts */
y_left /= 2;
y_right /= 2
for (ix = 0; ix < s.a_gch.length; ix++) {
gch = s.a_gch[ix]
switch (gch.type) {
case '<': /* left */
gch.y -= y_left
( run in 1.195 second using v1.01-cache-2.11-cpan-a5162978ef8 )