view release on metacpan or search on metacpan
of files that the 'classic' GUI suffers from.
Thanks Nick!
- ChordPro bundles free replacement fonts to be used instead of
the corefonts. No configs or settings needed.
!Functionality
- (PDF, page sort) Use sorttitle for page sorting.
- Images: Ignore align with x percentage. Issue warning.
- Detection of attributes in labels now requires quoting.
- Handle \u escapes for surrogates and extended chars (\u{...}).
- 'chordpro -A -A -A' will produce runtime info in JSON.
- Add '--convert-config' to convert config to new style.
- Add href property for images.
- New metadata: chordpro, chordpro.version and chordpro.songsource.
- Upgrade JSON::Relaxed to 0.096.
- Upgrade SVGPDF to 0.087. Enables transparent SVG images.
- Add independent horizontal and vertical scaling for images.
Requires Text::Layout 0.037_002.
- Upgrade Text::Layout to 0.038.
- Allow fret -1 in {define}, and 'x' in json config for consistency.
- Fix problem that titles-directive-ignore was ignored.
- (PDF) Fix problem that toc entries were not clickable.
- Fix issue #41 - Error in transposition of a recalled chorus.
- Fix issue #42 - Defining Chords Fails for Songbooks.
Song chord definitions were lost in multi-song songbooks except
for the last (or only) song.
- Fix schema validation for configs.
0.94 2018-01-23
- Allow \ { } and | to be escaped with \ in replacement strings.
- Fix problem that in-song chords caused CANNOT HAPPEN error.
- Add --filelist option to read song file names from files.
- Fix inconsistent handling of --lyrics-only in backends.
- Add html to list of recognized output types (even though
experimental). Note that the HTML backend is not yet included.
- Fix Chord/Chordii regression: Base frets in chord diagrams
should be arabic numbers, not roman.
- Pass unknown directives through to backends.
- Fix labels handling for ChordPro output.
- Fix problem that bass notes in chords were not transposed.
t/210_configs.t
t/211_config.t
t/212_config.t
t/21_basic02_crd.t
t/30_basic01_cho.t
t/310_basic.t
t/311_keys.t
t/312_context.t
t/313_null.t
t/314_data.t
t/315_escape.t
t/316_array.t
t/317_empty.t
t/31_basic02_cho.t
t/320_subst.t
t/321_subst.t
t/322_subst.t
t/323_null.t
t/380_roundtrip.t
t/400_kv.t
t/40_basic01_html.t
lib/ChordPro/Config.pm view on Meta::CPAN
my $cfg = Data::Properties->new;
$cfg->parse_file($from);
$new = $cfg->data;
}
else { # assume JSON, RJSON, RRJSON
$new = $parser->decode($data);
}
# And re-encode it using the schema.
my $res = $parser->encode( data => $new, pretty => 1,
nounicodeescapes => 1, schema => $schema );
# use DDP; p $res;
# Add trailer.
$res .= "\n// End of Config.\n";
# Write if out.
if ( $to && $to ne "-" ) {
open( my $fd, '>', $to )
or die("$to: $!\n");
print $fd $res;
$fd->close;
lib/ChordPro/Config/Properties.pm view on Meta::CPAN
Whitespace has no significance. A colon C<:> may be used instead of
C<=>. Lines that are blank or empty, and lines that start with C<#>
are ignored.
Property I<names> consist of one or more identifiers (series of
letters and digits) separated by periods.
Valid values are a plain text (whitespace, but not trailing, allowed),
a single-quoted string, or a double-quoted string. Single-quoted
strings allow embedded single-quotes by escaping them with a backslash
C<\>. Double-quoted strings allow common escapes like C<\n>, C<\t>,
C<\7>, C<\x1f> and C<\x{20cd}>.
Note that in plain text backslashes are taken literally. The following
alternatives yield the same results:
foo = a'\nb
foo = 'a\'\nb'
foo = "a'\\nb"
B<IMPORTANT:> All values are strings. These three are equivalent:
lib/ChordPro/Dumper.pm view on Meta::CPAN
no warnings qw( experimental::signatures );
use utf8;
package ChordPro::Dumper;
use Exporter qw(import);
our @EXPORT = qw(ddp);
use Data::Printer
hash_separator => " => ",
escape_chars => "nonascii",
print_escapes => 1,
scalar_quotes => "'",
caller_message_newline => 0,
string_max => 120,
class => { parents => 0,
linear_isa => 0,
show_methods => "none",
show_overloads => 0,
internals => 1 };
my $filters = [
lib/ChordPro/Output/Markdown.pm view on Meta::CPAN
foreach ( 0..$#{$elt->{chords}} ) {
$c_line .= chord( $elt->{chords}->[$_] ) . " ";
$t_line .= $phrases[$_];
my $d = length($c_line) - length($t_line);
$t_line .= "-" x $d if $d > 0;
$c_line .= " " x -$d if $d < 0;
} # this looks like setting the chords above the words.
s/\s+$// for ( $t_line, $c_line );
# main problem in markdown - a fixed position is only available in "Code escapes" so weather to set
# a tab or a double backticks (``) - i tend to the tab - so all lines with tabs are "together"
if ($c_line ne ""){ # Block-lines are not replacing initial spaces - as the are "code"
$t_line = $cp.$t_line." ";
$c_line = $cp.$c_line." ";
}
else{
$t_line = md_textline($cp.$t_line);
}
return $chords_under
? ( $t_line, $c_line )
lib/ChordPro/Output/PDF/Song.pm view on Meta::CPAN
sub prlabel {
my ( $ps, $label, $x, $y ) = @_;
return if $label eq "" || $ps->{_indent} == 0;
my $align = $ps->{labels}->{align};
my $font= $ps->{fonts}->{label} || $ps->{fonts}->{text};
$font->{size} ||= $font->{fd}->{size};
$ps->{pr}->setfont($font); # for strwidth.
# Now we have quoted strings we can have real newlines.
# Split on real and unescaped (old style) newlines.
for ( split( /\\n|\n/, $label ) ) {
my $label = $_;
if ( $align eq "right" ) {
my $avg_space_width = $ps->{pr}->strwidth("m");
$ps->{pr}->text( $label,
$x - $avg_space_width - $ps->{pr}->strwidth($label),
$y, $font );
}
elsif ( $align =~ /^cent(?:er|re)$/ ) {
$ps->{pr}->text( $label,
lib/ChordPro/Song.pm view on Meta::CPAN
$_ = shift(@$lines);
while ( /\\\Z/ && @$lines ) {
chop;
my $cont = shift(@$lines);
$$linecnt++;
$cont =~ s/^\s+//;
$_ .= $cont;
}
# Uncomment this to allow \uDXXX\uDYYY (surrogate) escapes.
s/ \\u(d[89ab][[:xdigit:]]{2})\\u(d[cdef][[:xdigit:]]{2})
/ pack('U*', 0x10000 + (hex($1) - 0xD800) * 0x400 + (hex($2) - 0xDC00) )
/igex;
# Uncomment this to allow \uXXXX escapes.
s/\\u([0-9a-f]{4})/chr(hex("0x$1"))/ige;
# Uncomment this to allow \u{XX...} escapes.
s/\\u\{([0-9a-f]+)\}/chr(hex("0x$1"))/ige;
$diag->{orig} = $_;
# Get rid of TABs.
s/\t/ /g;
if ( $config->{debug}->{echo} ) {
warn(sprintf("==[%3d]=> %s\n", $diag->{line}, $diag->{orig} ) );
}
lib/ChordPro/Wx/Main.pm view on Meta::CPAN
$self->can("OnSysColourChanged") );
$self->init_theme;
if ( @ARGV ) {
# use DDP;
use charnames ':full';
require _charnames;
push( @{$state{msgs}},
"ARGV: " . np(@ARGV,
show_unicode => 1,
escape_chars => 'nonascii',
unicode_charnames => 1 ) ) if 0;
my $arg = shift(@ARGV); # ignore rest
$arg = decode_utf8($arg);
push( @{$state{msgs}},
'DECODED: ' . np($arg,
show_unicode => 1,
escape_chars => 'nonascii',
unicode_charnames => 1 ) ) if 0;
if ( fs_test( 'd', $arg ) ) {
return 1 if $self->select_mode("sbexport")->open_dir($arg);
}
elsif ( !fs_test( 'r', $arg ) ) {
return 1 if $self->select_mode("editor")->newfile($arg);
Wx::MessageDialog->new( $self, "Error opening $arg",
"File Open Error",
wxOK | wxICON_ERROR )->ShowModal;
lib/ChordPro/Wx/RenderDialog.wxg view on Meta::CPAN
<?xml version="1.0"?>
<!-- generated by wxGlade 1.1.0 on Fri Nov 29 21:52:11 2024 -->
<application encoding="UTF-8" for_version="2.8" header_extension=".h" indent_amount="4" indent_symbol="space" is_template="0" language="perl" mark_blocks="1" option="0" overwrite="1" path="RenderDialog_wxg.pm" source_extension=".cpp" top_window="d_pr...
<object class="ChordPro::Wx::RenderDialog_wxg" name="d_prefs" base="EditDialog">
<extracode_pre>use ChordPro::Wx::Utils;</extracode_pre>
<title>Preview Tasks</title>
<style>wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</style>
<affirmative>b_ok</affirmative>
<escape>b_cancel</escape>
<object class="wxBoxSizer" name="sz_prefs_outer" base="EditBoxSizer">
<orient>wxVERTICAL</orient>
<object class="sizeritem">
<option>0</option>
<border>5</border>
<flag>wxALL|wxEXPAND</flag>
<object class="wxStaticText" name="label_3" base="EditStaticText">
<foreground>#0068d9</foreground>
<font>
<size>18</size>
lib/ChordPro/lib/Data/Printer/Theme/Zellner.pm view on Meta::CPAN
code => '#870087', # code references
glob => '#870087', # globs (usually file handles)
vstring => '#ff00ff', # version strings (v5.30.1, etc)
lvalue => '#000000', # lvalue label
format => '#000000', # format type
repeated => '#000000', # references to seen values
caller_info => '#878787', # details on what's being printed
weak => '#000000', # weak references flag
tainted => '#870000', # tainted flag
unicode => '#000000', # utf8 flag
escaped => '#ff00ff', # escaped characters (\t, \n, etc)
brackets => '#000000', # (), {}, []
separator => '#000000', # the "," between hash pairs, array elements, etc
quotes => '#000000', # q(")
unknown => '#878787', # any (potential) data type unknown to Data::Printer
};
}
1;
=pod
lib/ChordPro/lib/Data/Printer/Theme/Zellner.pm view on Meta::CPAN
code => '#870087', # code references
glob => '#870087', # globs (usually file handles)
vstring => '#ff00ff', # version strings (v5.30.1, etc)
lvalue => '#000000', # lvalue label
format => '#000000', # format type
repeated => '#000000', # references to seen values
caller_info => '#878787', # details on what's being printed
weak => '#000000', # weak references flag
tainted => '#870000', # tainted flag
unicode => '#000000', # utf8 flag
escaped => '#ff00ff', # escaped characters (\t, \n, etc)
brackets => '#000000', # (), {}, []
separator => '#000000', # the "," between hash pairs, array elements, etc
quotes => '#000000', # q(")
unknown => '#878787', # any (potential) data type unknown to Data::Printer
=head1 COPYRIGHT
E<copy> MMXXIII - Abe Timmerman <abeltje@cpan.org>
=head1 CONTRIBUTIONS
lib/ChordPro/lib/JSON/Relaxed.pm view on Meta::CPAN
Note that this is different from
"this is a \
long string"
which B<embeds> the newline into the string, and requires continuation
lines to start at the beginning of the line to prevent unwanted spaces.
Enabled by default, overruled by C<strict>.
=head2 Extended Unicode escapes
Unicode escapes in strings may contain an arbitrary number of hexadecimal
digits enclosed in braces:
\u{1d10e}
This eliminates the need to use
L<surrogates|https://unicode.org/faq/utf_bom.html#utf16-2>
to obtain the same character:
\uD834\uDD0E
lib/ChordPro/lib/JSON/Relaxed.pm view on Meta::CPAN
=over 4
=item *
Numbers will be output as numbers.
=item *
Strings will be output as unquoted strings if possible, quoted strings
otherwise. Non-latin characters will be output as C<\u> escapes.
When some of the quotes C<" ' `> are embedded the others will be tried
for the string, e.g. C<"a\"b"> will yield C<'a"b'>.
All quotes are equal, there is no difference in interpretation.
=item *
Boolean objects will be output as unquoted C<true> and C<false>.
=item *
lib/ChordPro/lib/JSON/Relaxed/Parser.pm view on Meta::CPAN
method is_quote ($c) {
$c =~ /^$p_quotes$/o;
}
# Numbers. A special case of unquoted strings.
my $p_number = q{[+-]?\d*\.?\d+(?:[Ee][+-]?\d+)?};
method pretokenize {
# \u escape (4 hexits)
my @p = ( qq<\\\\u[[:xdigit:]]{4}> );
# Any escaped char (strict mode).
if ( $strict ) {
push( @p, qq<\\\\.> );
}
# Otherwise, match \u{ ... } also.
else {
push( @p, qq<\\\\u\\{[[:xdigit:]]+\\}>, qq<\\\\[^u]> ); # escaped char
}
if ( $prp && !$strict ) {
# Add = to the reserved characters
$p_reserved = q<[,=:{}\[\]]>;
# Massage # comments into // comments without affecting position.
$data =~ s/^(\s*)#.(.*)$/$1\/\/$2/gm;
$data =~ s/^(\s*)#$/$1 /gm;
}
lib/ChordPro/lib/JSON/Relaxed/Parser.pm view on Meta::CPAN
method encode(%opts) {
my $schema = $opts{schema};
my $level = $opts{level} // 0;
my $rv = $opts{data}; # allow undef
my $indent = $opts{indent} // 2;
my $impoh = $opts{implied_outer_hash} // $implied_outer_hash;
my $ckeys = $opts{combined_keys} // $combined_keys;
my $prpmode = $opts{prp} // $prp;
my $pretty = $opts{pretty} // $pretty;
my $strict = $opts{strict} // $strict;
my $nouesc = $opts{nounicodeescapes} // 0;
if ( $strict ) {
$ckeys = $prpmode = $impoh = 0;
}
$schema = resolve( $schema, $schema ) if $schema;
my $s = "";
my $i = 0;
my $props = $schema->{properties};
lib/ChordPro/lib/JSON/Relaxed/Parser.pm view on Meta::CPAN
$v =~ s/\\/\\\\/g;
$v =~ s/\n/\\n/g;
$v =~ s/\r/\\r/g;
$v =~ s/\f/\\f/g;
$v =~ s/\013/\\v/g;
$v =~ s/\010/\\b/g;
$v =~ s/\t/\\t/g;
$v =~ s/([^ -ÿ])/sprintf( ord($1) < 0xffff ? "\\u%04x" : "\\u{%x}", ord($1))/ge unless $nouesc;
# Force quotes unless the string can be represented as unquoted.
if ( # contains escapes
$v ne $str
# not value-formed numeric
|| ( $v =~ /^$p_number$/ && 0+$v ne $v )
# contains reserved, quotes or spaces
|| $v =~ $p_reserved
|| $v =~ $p_quotes
|| $v =~ /\s/
|| $v =~ /^(true|false|null)$/
|| !length($v)
) {
lib/ChordPro/lib/JSON/Relaxed/Parser.pm view on Meta::CPAN
# If we have a key order, use this and delete.
my @ko = $rv->{" key order "}
? @{ delete($rv->{" key order "}) }
: sort(keys(%$rv));
# Dedup.
@ko = uniqstr(@ko);
my $ll = 0;
for ( @ko ) {
# This may be wrong if \ escapes or combined keys are involved.
$ll = length($_) if length($_) > $ll;
}
for ( @ko ) {
my $k = $_;
# Gather comments, if available.
my $comment;
if ( $props->{$k} ) {
$comment = $comments->($props->{$k});
lib/ChordPro/lib/JSON/Relaxed/Parser.pm view on Meta::CPAN
=cut
################ Strings ################
class JSON::Relaxed::String :isa(JSON::Relaxed::Token);
field $content :param = undef;
field $quote :accessor :param = undef;
# Quoted strings are assembled from complete substrings, so escape
# processing is done on the substrings. This prevents ugly things
# when unicode escapes are split across substrings.
# Unquotes strings are collected token by token, so escape processing
# can only be done on the complete string (on output).
ADJUST {
$content = $self->unescape($content) if defined($quote);
};
method append ($str) {
$str = $self->unescape($str) if defined $quote;
$content .= $str;
}
method content {
defined($quote) ? $content : $self->unescape($content);
}
# One regexp to match them all...
my $esc_quoted = qr/
\\([tnrfb]) # $1 : one char
| \\u\{([[:xdigit:]]+)\} # $2 : \u{XX...}
| \\u([Dd][89abAB][[:xdigit:]]{2}) # $3 : \uDXXX hi
\\u([Dd][c-fC-F][[:xdigit:]]{2}) # $4 : \uDXXX lo
| \\u([[:xdigit:]]{4}) # $5 : \uXXXX
| \\?(.) # $6
/xs;
# Special escapes (quoted strings only).
my %esc = (
'b' => "\b", # Backspace
'f' => "\f", # Form feed
'n' => "\n", # New line
'r' => "\r", # Carriage return
't' => "\t", # Tab
'v' => chr(11), # Vertical tab
);
method unescape ($str) {
return $str unless $str =~ /\\/;
my $convert = sub {
# Specials. Only for quoted strings.
if ( defined($1) ) {
return defined($quote) ? $esc{$1} : $1;
}
# Extended \u{XXX} character.
defined($2) and return chr(hex($2));
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
// 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]
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
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()
function end_tune() {
generate()
cfmt = sav.cfmt;
info = sav.info;
char_tb = sav.char_tb;
glovar = sav.glovar;
maps = sav.maps;
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
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()];
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
}
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
}
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
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]
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
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"
lib/ChordPro/res/config/config.schema view on Meta::CPAN
],
"default": "pct_chords"
}
}
}
}
},
{
"title": "Settings for the parser/preprocessor",
"type": "object",
"description": "For selected lines, you can specify a series of `{ \"target\" : \"xxx\", \"replace\" : \"yyy\" }`.\nEvery occurrence of \"xxx\" will be replaced by \"yyy\".\nUse \"pattern\" instead of \"target\" for regular expression replaceme...
"properties": {
"parser": {
"$ref": "#/definitions/parserspec",
"description": "Settings for the parser/preprocessor.\nFor selected lines, you can specify a series of \n{ \"target\" : \"xxx\", \"replace\" : \"yyy\" }\nEvery occurrence of \"xxx\" will be replaced by \"yyy\".\nUse \"pattern\" instead of \...
}
}
},
{
"title": "Text Output",
"type": "object",
t/10_files.t view on Meta::CPAN
}
}
is( 0+keys(%files), 0, "All files found" );
sub dd {
return;
use DDP;
diag np( $_[0],
show_unicode => 1,
escape_chars=>'nonascii',
unicode_charnames => 1,
$_[1] ? ( as => $_[1] ) : () );
}