abc2svg
Artifact [c44d2269ad]
Not logged in

Artifact c44d2269ad8c99cfb59b9796d2767b17354eb642:


#!/bin/sh
#
# abctopdf - converts ABC to PDF using abc2svg
# and one of
#	- a chrome/chromium compatible web browser
#	- weasyprint (https://weasyprint.org/)
#	- rsvg-convert (https://wiki.gnome.org/Projects/LibRsvg)
#
# Note: when weasyprint or rsvg-convert are used,
#	this script forces A4 as the paper size.
#	For an other size, change
#		'size: A4' and '--pageheight 29.7cm' with weasyprint
#		'%%pageheight 29.7cm' with rsvg-convert
#
# Copyright (C) 2019-2020 Jean-François Moine - License GPL3+

if [ $# -eq 0 ]; then
	cat << EOF
ABC to PDF translator using abc2svg
Usage:
  abctopdf [options] ABC_file [[options] ABC_file] [options] [-o output_file]
Arguments:
  options     ABC options (the last options are moved before the last file)
  ABC_file    ABC file
  output_file output file - default is "./abc.pdf"
EOF
	exit 1
fi

# choose a abc2svg batch script with an available JS interpreter
if [ ${0#./} != $0 ]; then
	bdir="./"
else
	bdir=`dirname $0`
	if [ $bdir != '.' ]; then
		bdir="$bdir/"
	else
		bdir=''
	fi
fi
# (sorted from fastest to slowest in ARM machines)
for c in qjs jsc js24 js52 js60 d8 abc2svg node end; do
	if [ $c = 'end' ]; then
		echo "$0: could not find a javascript interpreter - abort"
		exit 1
	fi
	if command -v $c >/dev/null 2>&1; then
		case $c in
		(qjs) c=abcqjs;;
		(jsc) c=abcjsc;;
		(js24) c=abcjs24;;
		(js52) c=abcjs52;;
		(js60) c=abcjs60;;
		(d8) c=abcv8;;
		(abc2svg) c=abc2svg;;
		(node) c=abcnode;;
		esac
		if command -v $c >/dev/null 2>&1; then
			abcscr=$c
			break
		fi
		if command -v $bdir$c >/dev/null 2>&1; then
			abcscr=$bdir$c
			break
		fi
	fi
done
echo "Using $abcscr"

# get the output file name (after '-o')
# default name
out='abc.pdf'
n=0
for a do
	if [ "$a" = "-o" ]; then
		n=1
	elif [ $n -eq 1 ]; then
		out=$a
		n=0
	else
		set -- "$@" "$a"
	fi
	shift
done

# check if some chromium-based browser is available
for c in 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
		$c --headless --disable-gpu --print-to-pdf=$out /tmp/o.html
		rm /tmp/o.html
		exit 0
	fi
done

# try weasyprint
if command -v weasyprint >/dev/null 2>&1; then
	echo "and weasyprint"

	rm -f /tmp/abc*.svg

# generate a (HTML+SVG) file
# then, extract the SVG images (pages) to /tmp/ and build a file.html
	cat > /tmp/abc.html <<EOF
<html> <head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<style type="text/css">
@page {
 size: A4;
 margin: 0;
}
</style>
</head>
<body>
EOF
n=0
command $abcscr --pageheight 29.7cm --fullsvg 1 --musicfont abc2svg "$@" | while read v; do
	case "$v" in
	"<svg"*)
		n=$(($n+1))
		fn="/tmp/abc$n.svg"
		echo $v > $fn
		echo "<img src=\"/tmp/abc${n}.svg\"/>" >> /tmp/abc.html
		;;
	"</svg"*)
		echo $v >> $fn
		fn=
		;;
	"</body"*)
		break;;
	*)
		if [ "X$fn" != "X" ]; then
			echo $v >> $fn
		else
			echo $v >> /tmp/abc.html
		fi
		;;
	esac
done
	echo '</body></html>' >> /tmp/abc.html

echo weasyprint -f pdf /tmp/abc.html $out
	weasyprint -f pdf /tmp/abc.html $out
	rm -f /tmp/abc*.svg /tmp/abc.html
	exit 0
fi

# last chance with rsvg-convert
# check the required programs
if ! command -v rsvg-convert >/dev/null 2>&1; then
	echo "$0: program 'rsvg-convert' not found - abort"
	exit 1
fi
echo "and rsvg-convert"

# build a ABC file (with JS script) to change the font and page definitions
# this solves the following problems:
# - the maintainer of librsvg does not want to handle the CSS shorthand 'font:'
# - rsvg-convert always gets the SVG images at 72ppi, ignoring the -p/-d values
# - the sets of SVG images per page must be converted to a set of one SVG per page
cat > /tmp/fix.abc << EOF
%%pageheight 29.7cm
%%fullsvg 1
%%musicfont abc2svg
%%beginjs
    var fix_out
function font_bug(str) {
	return str.replace(/font:.*?["};]/g, function(p) {	// "
	    var	w = p.slice(5, -1).match(/[^ \t"]+|".+?"/g), // "
		l = w.length,
		r = 'font-family:' + w[--l] + ';font-size:' + w[--l]
		while (--l >= 0) {
			switch (w[l]) {
			case 'italic':
				r += ';font-style:italic'
				break
			case 'oblique':
				r += ';font-style:oblique'
				break
			case 'bold':
				r += ';font-weight:bold'
				break
			}
		}
		return r + p[p.length - 1]
	})
}
// install
// - front-end
	fix_out = abc2svg.page.img_out
	abc2svg.page.img_out = function(page, p) {
		fix_out(page, font_bug(p))
	}
// - back-end
    var	page_h, page_out,
	page_cl = "",
	page_sty = ""

	// get the style from the image and remove it from the block
	function get_style(p) {
	    var	i,
		sty = p.match(/<style.*?>((.|\n)*?)<\/style>/)

		if (!sty)
			return p
		sty = sty[1].split('\n')
		if (!page_sty)
			page_sty = '<style type="text/css">\n</style>\n'
		for (i = 0; i < sty.length; i++) {
			if (page_sty.indexOf(sty[i]) < 0)
				page_sty = page_sty.replace('</style>\n',
						sty[i] + '\n</style>\n')
		}
		return p.replace(/<style(.|\n)*?\/style>\n/, '')
	} // get_style()

	// low level output
	abc.page.user_out = function(p) {
	    var	h, w,
		cfmt = abc.cfmt()

		switch (p.slice(0, 4)) {
		case "<div":			// new page
			page_h = 0
			page_out = ""
			break
		case "<svg":			// SVG image
			p = get_style(p)
			if (!page_cl) {
				h = p.match(/<svg(.|\n)*?class="([^"]*)"[^>]*>/) // "
				if (h)
					page_cl = h[2]
			}
			page_out += p
			    .replace(/<svg(.|\n)*?>/,
				'<g transform="translate(0, ' +
					page_h.toFixed(1) + ')">')
			    .replace('</svg>', '</g>\n')
			page_h += Number(p.match(/height="(\d+)px"/)[1])
			break
		case "</di":			// end of page
			w = cfmt.pagewidth
			h = cfmt.pageheight
			if (!page_cl)
				page_cl = "music"	// old version
			abc2svg.print('\
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
 xmlns:xlink="http://www.w3.org/1999/xlink"\n\
 color="black" class="' + page_cl + '" stroke-width=".7"\n\
 width="' +
				(w * .75).toFixed(0) + 'px" height="' +
				(h * .75).toFixed(0) + 'px" viewBox="0 0 ' +
					w.toFixed(0) + ' ' +
					h.toFixed(0) + '"' +
				(cfmt.bgcolor ?
					(' style="background-color: ' +
						cfmt.bgcolor + '"') : '') +
				'>\n' +
				page_sty +
				page_out + "</svg>")
			break
		}
	} // user_out()
// for tests
abc2svg.abort = function(e) {
abc2svg.printErr('abort: '+e.message+'\n'+e.stack)
abc2svg.quit()
}
%%endjs
EOF

# purge /tmp
rm -f /tmp/abc*.svg

# generate a (HTML+SVG) file with a abc2svg batch script
# then, extract the SVG images (pages) to /tmp
n=0
command $abcscr /tmp/fix.abc "$@" | while read v; do
	case "$v" in
	"<svg"*)
		n=$(($n+1))
		fn="/tmp/abc$n.svg"
		echo $v > $fn
		;;
	"</svg"*)
		echo $v >> $fn
		fn=
		;;
	"</body"*)
		break;;
	*)
		if [ "X$fn" != "X" ]; then
			echo $v >> $fn
		fi
		;;
	esac
done

# convert the SVG images to PDF
if [ -f /tmp/abc1.svg ]; then
 rsvg-convert -f pdf $(ls -rt /tmp/abc*.svg) -o $out
else
 echo 'Errors during the generation'
fi

# cleanup
rm -f /tmp/abc*.svg /tmp/fix.abc