File Coverage

blib/lib/ChordPro/Config.pm
Criterion Covered Total %
statement 391 548 71.3
branch 124 230 53.9
condition 68 144 47.2
subroutine 41 48 85.4
pod 0 23 0.0
total 624 993 62.8


line stmt bran cond sub pod time code
1             #! perl
2              
3             package main;
4              
5             our $options;
6             our $config;
7              
8             package ChordPro::Config;
9              
10 79     79   1550 use v5.26;
  79         332  
11 79     79   506 use utf8;
  79         196  
  79         669  
12 79     79   1935 use Carp;
  79         564  
  79         6075  
13 79     79   704 use feature qw( signatures );
  79         234  
  79         13391  
14 79     79   629 no warnings "experimental::signatures";
  79         216  
  79         3576  
15              
16 79     79   728 use App::Packager;
  79         253  
  79         494  
17 79     79   49442 use ChordPro;
  79         272  
  79         3320  
18 79     79   600 use ChordPro::Utils;
  79         177  
  79         8663  
19 79     79   1615 use File::LoadLines;
  79         25614  
  79         3384  
20 79     79   531 use File::Spec;
  79         232  
  79         1638  
21 79     79   59876 use JSON::PP ();
  79         1222139  
  79         3507  
22 79     79   699 use Scalar::Util qw(reftype);
  79         2694  
  79         5326  
23 79     79   635 use List::Util qw(any);
  79         177  
  79         5179  
24 79     79   47480 use Hash::Util;
  79         249997  
  79         678  
25              
26             =head1 NAME
27              
28             ChordPro::Config - Configurator.
29              
30             =head1 DESCRIPTION
31              
32             This module first establishes a well-defined (builtin) configuration.
33              
34             Then it processes the config files specified by the environment and
35             adds the information to the global $config hash.
36              
37             The configurations files are 'relaxed' JSON files. This means that
38             they may contain comments and trailing comma's.
39              
40             This module can be run standalone and will print the default config.
41              
42             =encoding utf8
43              
44             =cut
45              
46             #sub hmerge($$;$);
47             #sub clone($);
48             #sub default_config();
49              
50 197     197 0 1071 sub configurator ( $opts = undef ) {
  197         1797  
  197         1990  
51 197         5636 my $pp = JSON::PP->new->relaxed;
52              
53             # Test programs call configurator without options.
54             # Prepare a minimal config.
55 197 100       14258 unless ( $opts ) {
56 78         1586 my $cfg = $pp->decode( default_config() );
57 78         9161415 $config = $cfg;
58 78         459 $options = { verbose => 0 };
59 78         552 process_config( $cfg, "" );
60 78         414 $cfg->{settings}->{lineinfo} = 0;
61 78         1805 return $cfg;
62             }
63 119 100       655 if ( keys(%$opts) ) {
64 9   50     26 $options = { %{$options//{}}, %$opts };
  9         73  
65             }
66              
67 119         318 my @cfg;
68 119   100     806 my $verbose = $options->{verbose} //= 0;
69              
70             # Load defaults.
71 119 50       489 warn("Reading: \n") if $verbose > 1;
72 119         562 my $cfg = $pp->decode( default_config() );
73              
74             # Default first.
75 119         13869755 @cfg = prep_configs( $cfg, "" );
76             # Bubble default config to be the first.
77 119 50       688 unshift( @cfg, pop(@cfg) ) if @cfg > 1;
78              
79             # Collect other config files.
80             my $add_config = sub {
81 11     11   35 my $fn = shift;
82 11         46 $cfg = get_config( $fn );
83 11         86 push( @cfg, prep_configs( $cfg, $fn ) );
84 119         1101 };
85              
86 119         572 foreach my $c ( qw( sysconfig userconfig config ) ) {
87 357 100       1671 next if $options->{"no$c"};
88 9 100       69 if ( ref($options->{$c}) eq 'ARRAY' ) {
89 2         9 $add_config->($_) foreach @{ $options->{$c} };
  2         10  
90             }
91             else {
92 7 50       36 warn("Adding config for $c\n") if $verbose;
93 7         37 $add_config->( $options->{$c} );
94             }
95             }
96              
97             # Now we have a list of all config files. Weed out dups.
98 119         707 for ( my $a = 0; $a < @cfg; $a++ ) {
99 249 50 66     1615 if ( $a && $cfg[$a]->{_src} eq $cfg[$a-1]->{_src} ) {
100 0         0 splice( @cfg, $a, 1 );
101 0         0 redo;
102             }
103 249 50       886 print STDERR ("Config[$a]: ", $cfg[$a]->{_src}, "\n" )
104             if $verbose;
105             }
106              
107 119         464 $cfg = shift(@cfg);
108 119 50       646 warn("Process: $cfg->{_src}\n") if $verbose > 1;
109              
110             # Presets.
111 119 50       631 if ( $options->{reference} ) {
112 0         0 $cfg->{user}->{name} = "chordpro";
113 0         0 $cfg->{user}->{fullname} = ::runtimeinfo("short");
114             }
115             else {
116             $cfg->{user}->{name} =
117             lc( $ENV{USER} || $ENV{LOGNAME}
118 119   50     54209 || getlogin() || getpwuid($<) || "chordpro" );
119 119   50     801 $cfg->{user}->{fullname} = eval { (getpwuid($<))[6] } || "";
120             }
121              
122             # Add some extra entries to prevent warnings.
123 119         768 for ( qw(title subtitle footer) ) {
124 357 100       1641 next if exists($cfg->{pdf}->{formats}->{first}->{$_});
125 238         797 $cfg->{pdf}->{formats}->{first}->{$_} = "";
126             }
127 119         583 for my $ff ( qw(chord
128             diagram diagram_base chordfingers
129             comment comment_box comment_italic
130             tab text toc annotation label
131             empty footer grid grid_margin subtitle title) ) {
132 2142         3311 for ( qw(name file description size color background) ) {
133 12852   100     41292 $cfg->{pdf}->{fonts}->{$ff}->{$_} //= undef;
134             }
135             }
136              
137             my $backend_configurator =
138 119         2545 UNIVERSAL::can( $options->{backend}, "configurator" );
139              
140             # Apply config files
141 119         490 foreach my $new ( @cfg ) {
142 130         514 my $file = $new->{_src}; # for diagnostics
143             # Handle obsolete keys.
144 130 50       584 if ( exists $new->{pdf}->{diagramscolumn} ) {
145 0   0     0 $new->{pdf}->{diagrams}->{show} //= "right";
146 0         0 delete $new->{pdf}->{diagramscolumn};
147 0         0 warn("$file: pdf.diagramscolumn is obsolete, use pdf.diagrams.show instead\n");
148             }
149 130 50       773 if ( exists $new->{pdf}->{formats}->{default}->{'toc-title'} ) {
150 0   0     0 $new->{toc}->{title} //= $new->{pdf}->{formats}->{default}->{'toc-title'};
151 0         0 delete $new->{pdf}->{formats}->{default}->{'toc-title'};
152 0         0 warn("$file: pdf.formats.default.toc-title is obsolete, use toc.title instead\n");
153             }
154              
155             # Process.
156 130         430 local $::config = $cfg;
157 130         708 process_config( $new, $file );
158             # Merge final.
159 130         702 $cfg = hmerge( $cfg, $new );
160             }
161              
162             # Handle defines from the command line.
163 119         1211 $cfg = hmerge( $cfg, prp2cfg( $options->{define}, $cfg ) );
164              
165             # Sanitize added extra entries.
166 119         792 for ( qw(title subtitle footer) ) {
167             delete($cfg->{pdf}->{formats}->{first}->{$_})
168 357 100 50     2343 if ($cfg->{pdf}->{formats}->{first}->{$_} // 1) eq "";
169 357         881 for my $class ( qw(title first default) ) {
170 1071         2237 my $t = $cfg->{pdf}->{formats}->{$class}->{$_};
171 1071 100       2119 next unless $t;
172 833 50       1812 die("Config error in pdf.formats.$class.$_: not an array\n")
173             unless ref($t) eq 'ARRAY';
174 833 50       2156 if ( ref($t->[0]) ne 'ARRAY' ) {
175 833         1436 $t = [ $t ];
176             }
177 833         1335 my $tt = $_;
178 833         1453 for ( @$t) {
179 833 50 33     4585 die("Config error in pdf.formats.$class.$tt: ",
180             scalar(@$_), " fields instead of 3\n")
181             if @$_ && @$_ != 3;
182             }
183             }
184             }
185              
186 119 50       744 if ( $cfg->{pdf}->{fontdir} ) {
187 119         330 my @a;
188 119 50       641 if ( ref($cfg->{pdf}->{fontdir}) eq 'ARRAY' ) {
189 119         290 @a = @{ $cfg->{pdf}->{fontdir} };
  119         465  
190             }
191             else {
192 0         0 @a = ( $cfg->{pdf}->{fontdir} );
193             }
194 119         451 $cfg->{pdf}->{fontdir} = [];
195 119 50       1221 my $split = $^O =~ /^MS*/ ? qr(;) : qr(:);
196 119         683 foreach ( @a ) {
197 0         0 push( @{ $cfg->{pdf}->{fontdir} },
198 0         0 map { expand_tilde($_) } split( $split, $_ ) );
  0         0  
199             }
200             }
201             else {
202 0         0 $cfg->{pdf}->{fontdir} = [];
203             }
204              
205 119         371 my @allfonts = keys(%{$cfg->{pdf}->{fonts}});
  119         1407  
206 119         428 for my $ff ( @allfonts ) {
207 2142 100 66     9102 unless ( $cfg->{pdf}->{fonts}->{$ff}->{name}
      66        
208             || $cfg->{pdf}->{fonts}->{$ff}->{description}
209             || $cfg->{pdf}->{fonts}->{$ff}->{file} ) {
210 952         2292 delete( $cfg->{pdf}->{fonts}->{$ff} );
211 952         1554 next;
212             }
213 1190   50     4670 $cfg->{pdf}->{fonts}->{$ff}->{color} //= "foreground";
214 1190   100     4227 $cfg->{pdf}->{fonts}->{$ff}->{background} //= "background";
215 1190         2006 for ( qw(name file description size) ) {
216             delete( $cfg->{pdf}->{fonts}->{$ff}->{$_} )
217 4760 100       11107 unless defined( $cfg->{pdf}->{fonts}->{$ff}->{$_} );
218             }
219             }
220              
221 119 50       1357 if ( defined $options->{diagrams} ) {
    50          
    50          
222             warn( "Invalid value for diagrams: ",
223             $options->{diagrams}, "\n" )
224 0 0       0 unless $options->{diagrams} =~ /^(all|none|user)$/i;
225 0         0 $cfg->{diagrams}->{show} = lc $options->{'diagrams'};
226             }
227             elsif ( defined $options->{'user-chord-grids'} ) {
228             $cfg->{diagrams}->{show} =
229 0 0       0 $options->{'user-chord-grids'} ? "user" : 0;
230             }
231             elsif ( defined $options->{'chord-grids'} ) {
232             $cfg->{diagrams}->{show} =
233 0 0       0 $options->{'chord-grids'} ? "all" : 0;
234             }
235              
236 119         451 for ( qw( transpose transcode decapo lyrics-only strict ) ) {
237 595 100       1741 next unless defined $options->{$_};
238 18         82 $cfg->{settings}->{$_} = $options->{$_};
239             }
240              
241 119         506 for ( "front-matter", "back-matter" ) {
242 238 100       880 next unless defined $options->{$_};
243 6         23 $cfg->{pdf}->{$_} = $options->{$_};
244             }
245              
246 119 50       529 if ( defined $options->{'chord-grids-sorted'} ) {
247 0         0 $cfg->{diagrams}->{sorted} = $options->{'chord-grids-sorted'};
248             }
249              
250             # For convenience...
251 119         835 bless( $cfg, __PACKAGE__ );
252              
253 119 50       547 return $cfg if $options->{'cfg-print'};
254              
255             # Backend specific configs.
256 119 100       528 $backend_configurator->($cfg) if $backend_configurator;
257              
258             # Locking the hash is mainly for development.
259 119         1105 $cfg->lock;
260              
261 119 50       232775 if ( $options->{verbose} > 1 ) {
262 0   0     0 my $cp = ChordPro::Chords::get_parser() // "";
263 0         0 warn("Parsers:\n");
264 0         0 while ( my ($k, $v) = each %{ChordPro::Chords::Parser->parsers} ) {
  0         0  
265 0 0       0 warn( " $k",
266             $v eq $cp ? " (active)": "",
267             "\n");
268             }
269             }
270              
271 119         46538 return $cfg;
272             }
273              
274             # Get the decoded contents of a single config file.
275 132     132 0 386 sub get_config ( $file ) {
  132         317  
  132         281  
276 132 50       457 Carp::confess("FATAL: Undefined config") unless defined $file;
277 132         463 my $verbose = $options->{verbose};
278 132 50       513 warn("Reading: $file\n") if $verbose > 1;
279 132         630 $file = expand_tilde($file);
280              
281 132 50       1288 if ( $file =~ /\.json$/i ) {
    0          
282 132 50       11015 if ( open( my $fd, "<:raw", $file ) ) {
283 132         2046 my $pp = JSON::PP->new->relaxed;
284 132         10490 my $new = $pp->decode( loadlines( $fd, { split => 0 } ) );
285 132         32237770 precheck( $new, $file );
286 132         5673 close($fd);
287 132         3275 return $new;
288             }
289             else {
290 0         0 die("Cannot open config $file [$!]\n");
291             }
292             }
293             elsif ( $file =~ /\.prp$/i ) {
294 0 0       0 if ( -e -f -r $file ) {
295 0         0 require ChordPro::Config::Properties;
296 0         0 my $cfg = new Data::Properties;
297 0         0 $cfg->parse_file($file);
298 0         0 return $cfg->data;
299             }
300             else {
301 0         0 die("Cannot open config $file [$!]\n");
302             }
303             }
304             else {
305 0         0 Carp::confess("Unrecognized config type: $file\n");
306             }
307             }
308              
309             # Check config for includes, and prepend them.
310 249     249 0 1228 sub prep_configs ( $cfg, $src ) {
  249         634  
  249         966  
  249         588  
311 249         1028 $cfg->{_src} = $src;
312              
313 249         702 my @res;
314              
315             # If there are includes, add them first.
316 249         8090 my ( $vol, $dir, undef ) = File::Spec->splitpath($cfg->{_src});
317 249         964 foreach my $c ( @{ $cfg->{include} } ) {
  249         1433  
318             # Check for resource names.
319 119 50 0     677 if ( $c !~ m;[/.]; ) {
    0          
320 119         828 $c = ::rsc_or_file( $c, "config" );
321             }
322             elsif ( $dir ne ""
323             && !File::Spec->file_name_is_absolute($c) ) {
324             # Prepend dir of the caller, if needed.
325 0         0 $c = File::Spec->catpath( $vol, $dir, $c );
326             }
327 119         755 my $cfg = get_config($c);
328             # Recurse.
329 119         927 push( @res, prep_configs( $cfg, $c ) );
330             }
331              
332             # Push this and return.
333 249         652 push( @res, $cfg );
334 249         1052 return @res;
335             }
336              
337 208     208 0 576 sub process_config ( $cfg, $file ) {
  208         517  
  208         549  
  208         482  
338 208         693 my $verbose = $options->{verbose};
339              
340 208 50       923 warn("Process: $file\n") if $verbose > 1;
341              
342 208 100       1116 if ( $cfg->{tuning} ) {
343 197         1714 my $res =
344             ChordPro::Chords::set_tuning( $cfg );
345 197 50       913 warn( "Invalid tuning in config: ", $res, "\n" ) if $res;
346 197         826 $cfg->{_tuning} = $cfg->{tuning};
347 197         700 $cfg->{tuning} = [];
348             }
349              
350 208         1285 ChordPro::Chords::reset_parser;
351 208         2811 ChordPro::Chords::Parser->reset_parsers;
352 208         1014 local $::config = hmerge( $::config, $cfg );
353 208 100       1631 if ( $cfg->{chords} ) {
354 197         2073 ChordPro::Chords::push_parser($cfg->{notes}->{system});
355 197         744 my $c = $cfg->{chords};
356 197 50 66     1480 if ( @$c && $c->[0] eq "append" ) {
357 0         0 shift(@$c);
358             }
359 197         815 foreach ( @$c ) {
360 49742         106598 my $res =
361             ChordPro::Chords::add_config_chord($_);
362             warn( "Invalid chord in config: ",
363 49742 50       128278 $_->{name}, ": ", $res, "\n" ) if $res;
364             }
365 197 50       1209 if ( $verbose > 1 ) {
366 0         0 warn( "Processed ", scalar(@$c), " chord entries\n");
367 0         0 warn( "Totals: ",
368             ChordPro::Chords::chord_stats(), "\n" );
369             }
370 197         985 $cfg->{_chords} = delete $cfg->{chords};
371 197         973 ChordPro::Chords::pop_parser();
372             }
373             }
374              
375 0     0 0 0 sub config_final ( $delta ) {
  0         0  
  0         0  
376 0         0 $options->{'cfg-print'} = 1;
377 0         0 my $cfg = configurator($options);
378              
379 0 0       0 if ( $delta ) {
380 0         0 my $pp = JSON::PP->new->relaxed;
381 0         0 my $def = $pp->decode( default_config() );
382 0         0 $cfg->reduce($def);
383             }
384 0         0 $cfg->unlock;
385 0         0 $cfg->{tuning} = delete $cfg->{_tuning};
386 0 0 0     0 delete($cfg->{tuning}) if $delta && !defined($cfg->{tuning});
387 0         0 $cfg->{chords} = delete $cfg->{_chords};
388 0         0 delete $cfg->{chords};
389 0         0 delete $cfg->{_src};
390 0         0 $cfg->lock;
391              
392 0 0       0 if ( $ENV{CHORDPRO_CFGPROPS} ) {
393 0         0 cfg2props($cfg);
394             }
395             else {
396 0         0 my $pp = JSON::PP->new->canonical->indent(4)->pretty;
397 0         0 $pp->encode({%$cfg});
398             }
399             }
400              
401 0     0 0 0 sub config_default () {
  0         0  
402 0 0       0 if ( $ENV{CHORDPRO_CFGPROPS} ) {
403 0         0 my $pp = JSON::PP->new->relaxed;
404 0         0 my $cfg = $pp->decode( default_config() );
405 0         0 cfg2props($cfg);
406             }
407             else {
408 0         0 default_config();
409             }
410             }
411              
412             # Config in properties format.
413              
414 0     0 0 0 sub cfg2props ( $o, $path ) {
  0         0  
  0         0  
  0         0  
415 0   0     0 $path //= "";
416 0         0 my $ret = "";
417 0 0       0 if ( !defined $o ) {
    0          
    0          
    0          
418 0         0 $ret .= "$path: undef\n";
419             }
420             elsif ( UNIVERSAL::isa( $o, 'HASH' ) ) {
421 0 0       0 $path .= "." unless $path eq "";
422 0         0 for ( sort keys %$o ) {
423 0         0 $ret .= cfg2props( $o->{$_}, $path . $_ );
424             }
425             }
426             elsif ( UNIVERSAL::isa( $o, 'ARRAY' ) ) {
427 0 0       0 $path .= "." unless $path eq "";
428 0         0 for ( my $i = 0; $i < @$o; $i++ ) {
429 0         0 $ret .= cfg2props( $o->[$i], $path . "$i" );
430             }
431             }
432             elsif ( $o =~ /^\d+$/ ) {
433 0         0 $ret .= "$path: $o\n";
434             }
435             else {
436 0         0 $o =~ s/\\/\\\\/g;
437 0         0 $o =~ s/"/\\"/g;
438 0         0 $o =~ s/\n/\\n/;
439 0         0 $o =~ s/\t/\\t/;
440 0         0 $o =~ s/([^\x00-\xff])/sprintf("\\x{%x}", ord($1))/ge;
  0         0  
441 0         0 $ret .= "$path: \"$o\"\n";
442             }
443              
444 0         0 return $ret;
445             }
446              
447             # Locking/unlocking. Locking the hash is mainly for development, to
448             # trap accidental modifications and typos.
449              
450 302     302 0 638 sub lock ( $self ) {
  302         621  
  302         589  
451 302         1451 Hash::Util::lock_hashref_recurse($self);
452             }
453              
454 223     223 0 497 sub unlock ( $self ) {
  223         454  
  223         397  
455 223         1092 Hash::Util::unlock_hashref_recurse($self);
456             }
457              
458 16     16 0 31 sub is_locked ( $self ) {
  16         30  
  16         24  
459 16         73 Hash::Util::hashref_locked($self);
460             }
461              
462             # Augment / Reduce.
463              
464 15     15 0 1693 sub augment ( $self, $hash ) {
  15         33  
  15         27  
  15         25  
465              
466 15         57 my $locked = $self->is_locked;
467 15 100       171 $self->unlock if $locked;
468              
469 15         22730 $self->_augment( $hash, "" );
470              
471 15 100       75 $self->lock if $locked;
472              
473 15         24471 $self;
474             }
475              
476              
477 43     43   68 sub _augment ( $self, $hash, $path ) {
  43         58  
  43         67  
  43         67  
  43         57  
478              
479 43         126 for my $key ( keys(%$hash) ) {
480              
481             warn("Config augment error: unknown item $path$key\n")
482 45 0 33     159 unless exists $self->{$key}
      33        
      0        
483             || $path =~ /^pdf\.(?:info|fonts|fontconfig)\./
484             || $path =~ /^meta\./
485             || $key =~ /^_/;
486              
487             # Hash -> Hash.
488             # Hash -> Array.
489 45 100 66     223 if ( ref($hash->{$key}) eq 'HASH' ) {
    100          
490 28 50       84 if ( ref($self->{$key}) eq 'HASH' ) {
    0          
491              
492             # Hashes. Recurse.
493 28         139 _augment( $self->{$key}, $hash->{$key}, "$path$key." );
494             }
495             elsif ( ref($self->{$key}) eq 'ARRAY' ) {
496              
497             # Hash -> Array.
498             # Update single array element using a hash index.
499 0         0 foreach my $ix ( keys(%{$hash->{$key}}) ) {
  0         0  
500 0 0       0 die unless $ix =~ /^\d+$/;
501 0         0 $self->{$key}->[$ix] = $hash->{$key}->{$ix};
502             }
503             }
504             else {
505             # Overwrite.
506 0         0 $self->{$key} = $hash->{$key};
507             }
508             }
509              
510             # Array -> Array.
511             elsif ( ref($hash->{$key}) eq 'ARRAY'
512             and ref($self->{$key}) eq 'ARRAY' ) {
513              
514             # Arrays. Overwrite or append.
515 2 50       4 if ( @{$hash->{$key}} ) {
  2         6  
516 2         3 my @v = @{ $hash->{$key} };
  2         6  
517 2 50       8 if ( $v[0] eq "append" ) {
    100          
518 0         0 shift(@v);
519             # Append the rest.
520 0         0 push( @{ $self->{$key} }, @v );
  0         0  
521             }
522             elsif ( $v[0] eq "prepend" ) {
523 1         3 shift(@v);
524             # Prepend the rest.
525 1         2 unshift( @{ $self->{$key} }, @v );
  1         5  
526             }
527             else {
528             # Overwrite.
529 1         6 $self->{$key} = $hash->{$key};
530             }
531             }
532             else {
533             # Overwrite.
534 0         0 $self->{$key} = $hash->{$key};
535             }
536             }
537              
538             else {
539             # Overwrite.
540 15         59 $self->{$key} = $hash->{$key};
541             }
542             }
543              
544 43         89 $self;
545             }
546              
547 79     79   460726 use constant DEBUG => 0;
  79         253  
  79         212526  
548              
549 1     1 0 1467 sub reduce ( $self, $hash ) {
  1         3  
  1         5  
  1         2  
550              
551 1         4 my $locked = $self->is_locked;
552              
553 1         11 warn("O: ", qd($hash,1), "\n") if DEBUG;
554 1         2 warn("N: ", qd($self,1), "\n") if DEBUG;
555 1         12 my $state = _reduce( $self, $hash, "" );
556              
557 1 50       3 $self->lock if $locked;
558              
559 1         2 warn("== ", qd($self,1), "\n") if DEBUG;
560 1         4 return $self;
561             }
562              
563 68     68   90 sub _ref ( $self ) {
  68         90  
  68         126  
564 68   66     355 reftype($self) // ref($self);
565             }
566              
567 6     6   9 sub _reduce ( $self, $orig, $path ) {
  6         9  
  6         8  
  6         12  
  6         7  
568              
569 6         10 my $state;
570              
571 6 100 66     13 if ( _ref($self) eq 'HASH' && _ref($orig) eq 'HASH' ) {
572              
573 3         6 warn("D: ", qd($self,1), "\n") if DEBUG && !%$orig;
574 3 50       8 return 'D' unless %$orig;
575              
576 3         11 my %hh = map { $_ => 1 } keys(%$self), keys(%$orig);
  12         26  
577 3         16 for my $key ( sort keys(%hh) ) {
578              
579             warn("Config reduce error: unknown item $path$key\n")
580 6 50 33     21 unless exists $self->{$key}
581             || $key =~ /^_/;
582              
583 6 50       14 unless ( defined $orig->{$key} ) {
584 0         0 warn("D: $path$key\n") if DEBUG;
585 0         0 delete $self->{$key};
586 0   0     0 $state //= 'M';
587 0         0 next;
588             }
589              
590             # Hash -> Hash.
591 6 100 66     13 if ( _ref($orig->{$key}) eq 'HASH'
    100 66        
    50 100        
      50        
      50        
      33        
      33        
      33        
      33        
      33        
592             and _ref($self->{$key}) eq 'HASH'
593             or
594             _ref($orig->{$key}) eq 'ARRAY'
595             and _ref($self->{$key}) eq 'ARRAY' ) {
596             # Recurse.
597 4         25 my $m = _reduce( $self->{$key}, $orig->{$key}, "$path$key." );
598 4 50 33     18 delete $self->{$key} if $m eq 'D' || $m eq 'I';
599 4 50 100     17 $state //= 'M' if $m ne 'I';
600             }
601              
602             elsif ( ($self->{$key}//'') eq ($orig->{$key}//'') ) {
603 1         4 warn("I: $path$key\n") if DEBUG;
604 1         3 delete $self->{$key};
605             }
606             elsif ( !defined($self->{$key})
607             and _ref($orig->{$key}) eq 'ARRAY'
608 0         0 and !@{$orig->{$key}}
609             or
610             !defined($orig->{$key})
611             and _ref($self->{$key}) eq 'ARRAY'
612 0         0 and !@{$self->{$key}} ) {
613             # Properties input [] yields undef.
614 0         0 warn("I: $path$key\n") if DEBUG;
615 0         0 delete $self->{$key};
616             }
617             else {
618             # Overwrite.
619 1         2 warn("M: $path$key => $self->{$key}\n") if DEBUG;
620 1   50     6 $state //= 'M';
621             }
622             }
623 3   50     15 return $state // 'I';
624             }
625              
626 3 50 33     7 if ( _ref($self) eq 'ARRAY' && _ref($orig) eq 'ARRAY' ) {
627              
628             # Arrays.
629 3 100   5   20 if ( any { _ref($_) } @$self ) {
  5         12  
630             # Complex arrays. Recurse.
631 1         19 for ( my $key = 0; $key < @$self; $key++ ) {
632 1         13 my $m = _reduce( $self->[$key], $orig->[$key], "$path$key." );
633             #delete $self->{$key} if $m eq 'D'; # TODO
634 1 50 50     9 $state //= 'M' if $m ne 'I';
635             }
636 1   50     5 return $state // 'I';
637             }
638              
639             # Simple arrays (only scalar values).
640 2 100       17 if ( my $dd = @$self - @$orig ) {
641 1         10 $path =~ s/\.$//;
642 1 50       14 if ( $dd > 0 ) {
643             # New is larger. Check for prepend/append.
644             # Deal with either one, not both. Maybe later.
645 1         3 my $t;
646 1         6 for ( my $ix = 0; $ix < @$orig; $ix++ ) {
647 1 50       5 next if $orig->[$ix] eq $self->[$ix];
648 1         3 $t++;
649 1         2 last;
650             }
651 1 50       4 unless ( $t ) {
652 0         0 warn("M: $path append @{$self}[-$dd..-1]\n") if DEBUG;
653 0         0 splice( @$self, 0, $dd, "append" );
654 0         0 return 'M';
655             }
656 1         2 undef $t;
657 1         4 for ( my $ix = $dd; $ix < @$self; $ix++ ) {
658 2 50       8 next if $orig->[$ix-$dd] eq $self->[$ix];
659 0         0 $t++;
660 0         0 last;
661             }
662 1 50       3 unless ( $t ) {
663 1         2 warn("M: $path prepend @{$self}[0..$dd-1]\n") if DEBUG;
664 1         3 splice( @$self, $dd );
665 1         4 unshift( @$self, "prepend" );
666 1         4 return 'M';
667             }
668 0         0 warn("M: $path => @$self\n") if DEBUG;
669 0         0 $state = 'M';
670             }
671             else {
672 0         0 warn("M: $path => @$self\n") if DEBUG;
673 0         0 $state = 'M';
674             }
675 0   0     0 return $state // 'I';
676             }
677              
678             # Equal length arrays with scalar values.
679 1         4 my $t;
680 1         10 for ( my $ix = 0; $ix < @$orig; $ix++ ) {
681 1 50       6 next if $orig->[$ix] eq $self->[$ix];
682 1         2 warn("M: $path$ix => $self->[$ix]\n") if DEBUG;
683 1         3 $t++;
684 1         2 last;
685             }
686 1 50       3 if ( $t ) {
687 1         4 warn("M: $path\n") if DEBUG;
688 1         4 return 'M';
689             }
690 0         0 warn("I: $path\[]\n") if DEBUG;
691 0         0 return 'I';
692             }
693              
694             # Two scalar values.
695 0         0 $path =~ s/\.$//;
696 0 0       0 if ( $self eq $orig ) {
697 0         0 warn("I: $path\n") if DEBUG;
698 0         0 return 'I';
699             }
700              
701 0         0 warn("M $path $self\n") if DEBUG;
702 0         0 return 'M';
703             }
704              
705 6938     6938 0 9240 sub hmerge( $left, $right, $path = "" ) {
  6938         9201  
  6938         8563  
  6938         9843  
  6938         8576  
706              
707             # Merge hashes. Right takes precedence.
708             # Based on Hash::Merge::Simple by Robert Krimen.
709              
710 6938         50306 my %res = %$left;
711              
712 6938         18772 for my $key ( keys(%$right) ) {
713              
714             warn("Config error: unknown item $path$key\n")
715 25129 50 66     52525 unless exists $res{$key}
      66        
      33        
      33        
      33        
      33        
716             || $path eq "pdf.fontconfig."
717             || $path =~ /^pdf\.(?:info|fonts)\./
718             || $path =~ /^meta\./
719             || $path =~ /^delegates\./
720             || $path =~ /^debug\./
721             || $key =~ /^_/;
722              
723 25129 100 66     77287 if ( ref($right->{$key}) eq 'HASH'
    100 100        
724             and
725             ref($res{$key}) eq 'HASH' ) {
726              
727             # Hashes. Recurse.
728 6479         18638 $res{$key} = hmerge( $res{$key}, $right->{$key}, "$path$key." );
729             }
730             elsif ( ref($right->{$key}) eq 'ARRAY'
731             and
732             ref($res{$key}) eq 'ARRAY' ) {
733             warn("AMERGE $key: ",
734             join(" ", map { qq{"$_"} } @{ $res{$key} }),
735             " + ",
736 2849         4084 join(" ", map { qq{"$_"} } @{ $right->{$key} }),
737             " \n") if 0;
738             # Arrays. Overwrite or append.
739 2849 100       3774 if ( @{$right->{$key}} ) {
  2849         6228  
740 1493         2272 my @v = @{ $right->{$key} };
  1493         8500  
741 1493 50       4460 if ( $v[0] eq "append" ) {
    50          
742 0         0 shift(@v);
743             # Append the rest.
744             warn("PRE: ",
745             join(" ", map { qq{"$_"} } @{ $res{$key} }),
746             " + ",
747 0         0 join(" ", map { qq{"$_"} } @v),
748             "\n") if 0;
749 0         0 push( @{ $res{$key} }, @v );
  0         0  
750             warn("POST: ",
751 0         0 join(" ", map { qq{"$_"} } @{ $res{$key} }),
752             "\n") if 0;
753             }
754             elsif ( $v[0] eq "prepend" ) {
755 0         0 shift(@v);
756             # Prepend the rest.
757 0         0 unshift( @{ $res{$key} }, @v );
  0         0  
758             }
759             else {
760             # Overwrite.
761 1493         4579 $res{$key} = $right->{$key};
762             }
763             }
764             else {
765             # Overwrite.
766 1356         2877 $res{$key} = $right->{$key};
767             }
768             }
769             else {
770             # Overwrite.
771 15801         25077 $res{$key} = $right->{$key};
772             }
773             }
774              
775 6938         18794 return \%res;
776             }
777              
778 40     40 0 93 sub clone ( $source ) {
  40         86  
  40         63  
779              
780 40 50       120 return if not defined($source);
781              
782 79     79   835 use Storable;
  79         192  
  79         73435  
783 40         52391 my $clone = Storable::dclone($source);
784 40         424 $clone->unlock;
785 40         72222 return $clone;
786              
787             }
788              
789 132     132 0 475 sub precheck ( $cfg, $file ) {
  132         346  
  132         364  
  132         314  
790              
791 132         614 my $verbose = $options->{verbose};
792 132 50       720 warn("Verify config \"$file\"\n") if $verbose > 1;
793 132         343 my $p;
794             $p = sub {
795 283271     283271   458710 my ( $o, $path ) = @_;
796 283271   100     445171 $path //= "";
797 283271 50       995147 if ( !defined $o ) {
    100          
    100          
798 0         0 warn("$file: Undefined config \"$path\" (using 'false')\n");
799 0         0 $_[0] = '';
800             }
801             elsif ( UNIVERSAL::isa( $o, 'HASH' ) ) {
802 50244 100       93513 $path .= "." unless $path eq "";
803 50244         199026 for ( sort keys %$o ) {
804 119293         248837 $p->( $o->{$_}, $path . $_ );
805             }
806             }
807             elsif ( UNIVERSAL::isa( $o, 'ARRAY' ) ) {
808 19175 50       37237 $path .= "." unless $path eq "";
809 19175         39877 for ( my $i = 0; $i < @$o; $i++ ) {
810 163846         360897 $p->( $o->[$i], $path . "$i" );
811             }
812             }
813 132         1986 };
814              
815 132         586 $p->($cfg);
816             }
817              
818              
819             ## Data::Properties compatible API.
820             #
821             # Note: Lookup always takes the context into account.
822             # Note: Always signals undefined values.
823              
824             my $prp_context = "";
825              
826 12     12 0 16 sub get_property ( $p, $prp, $def = undef ) {
  12         19  
  12         15  
  12         18  
  12         21  
827 12 100       63 for ( split( /\./,
828             $prp_context eq ""
829             ? $prp
830             : "$prp_context.$prp" ) ) {
831 34 100       99 if ( /^\d+$/ ) {
832 5 50       11 die("No config $prp\n") unless _ref($p) eq 'ARRAY';
833 5         18 $p = $p->[$_];
834             }
835             else {
836 29 50       52 die("No config $prp\n") unless _ref($p) eq 'HASH';
837 29         78 $p = $p->{$_};
838             }
839             }
840 12   66     38 $p //= $def;
841 12 50       25 die("No config $prp\n") unless defined $p;
842 12         63 $p;
843             }
844              
845             *gps = \&get_property;
846              
847             sub set_property {
848 0     0 0 0 ...;
849             }
850              
851 2     2 0 6 sub set_context ( $self, $ctx = "" ) {
  2         3  
  2         5  
  2         3  
852 2         5 $prp_context = $ctx;
853             }
854              
855 0     0 0 0 sub get_context () {
  0         0  
856 0         0 $prp_context;
857             }
858              
859             # For testing
860 79     79   753 use base qw(Exporter);
  79         213  
  79         51683  
861             our @EXPORT = qw( _c );
862 12     12   2044 sub _c ( @args ) { $::config->gps(@args) }
  12         29  
  12         16  
  12         34  
863              
864             # Get the raw contents of the builtin (default) config.
865 197     197 0 1755 sub default_config () {
  197         3018  
866 197         3646 return <<'End_Of_Config';
867             // Configuration for ChordPro.
868             //
869             // This is a relaxed JSON document, so comments are possible.
870              
871             {
872             // Includes. These are processed first, before the rest of
873             // the config file.
874             //
875             // "include" takes a list of either filenames or preset names.
876             // "include" : [ "modern1", "lib/mycfg.json" ],
877             "include" : [ "guitar" ],
878              
879             // General settings, often changed by configs and command line.
880             "settings" : {
881             // Strict behaviour.
882             "strict" : true,
883             // Obsolete -- lineinfo is always included.
884             "lineinfo" : true,
885             // Titles flush: default center.
886             "titles" : "center",
887             // Columns, default one.
888             "columns" : 1,
889             // Suppress empty chord lines.
890             // Overrides the -a (--single-space) command line options.
891             "suppress-empty-chords" : true,
892             // Suppress blank lyrics lines.
893             "suppress-empty-lyrics" : true,
894             // Suppress chords.
895             // Overrides --lyrics-only command line option.
896             "lyrics-only" : false,
897             // Memorize chords in sections, to be recalled by [^].
898             "memorize" : false,
899             // Format to show chord names. May contain markup.
900             // "chord-format" : "%{root}%{qual|%{}}%{ext|%{}}%{bass|/%{}}",
901             "chord-format" : "%{root|%{}%{qual|%{}}%{ext|%{}}%{bass|/%{}}|%{name}}",
902             // Chords inline.
903             // May be a string containing pretext %s posttext.
904             // Defaults to "[%s]" if set to a value that doesn't contain "%s".
905             "inline-chords" : false,
906             // Same, for annotations. Ignored unless inline-chords is set.
907             // Must be a string containing pretext %s posttext.
908             // Default is "%s".
909             "inline-annotations" : "%s",
910             // Chords under the lyrics.
911             "chords-under" : false,
912             // Transposing.
913             "transpose" : 0,
914             // Transcoding.
915             "transcode" : "",
916             // Always decapoize.
917             "decapo" : false,
918             // Chords parsing strategy.
919             // Strict (only known) or relaxed (anything that looks sane).
920             "chordnames": "strict",
921             // Allow note names in [].
922             "notenames" : false,
923             // Always replace chords by their canonical form.
924             "chords-canonical" : false,
925             // If false, chorus labels are used as tags.
926             "choruslabels" : true,
927             // Substitute Unicode sharp/flats in chord names.
928             // Will fallback to ChordProSymbols the font doesn't have the glyphs.
929             "truesf" : false,
930             },
931              
932             // Metadata.
933             // For these keys you can use {meta key ...} as well as {key ...}.
934             // If strict is nonzero, only the keys named here are allowed.
935             // If strict is zero, {meta ...} will accept any key.
936             // Important: "title" and "subtitle" must always be in this list.
937             // The separator is used to concatenate multiple values.
938             // If autosplit is true, the separator is also used to split
939             // values upon input.
940             "metadata" : {
941             "keys" : [ "title", "subtitle",
942             "artist", "composer", "lyricist", "arranger",
943             "album", "copyright", "year",
944             "sorttitle",
945             "key", "time", "tempo", "capo", "duration" ],
946             "strict" : true,
947             "separator" : "; ",
948             "autosplit" : true,
949             },
950             // Globally defined (added) meta data,
951             // This is explicitly NOT intended for the metadata items above.
952             "meta" : {
953             },
954              
955             // Assets.
956             "assets" : {},
957              
958             // Dates. Format is a strftime template.
959             "dates" : {
960             "today" : {
961             "format" : "%A, %B %e, %Y"
962             }
963             },
964              
965             // User settings. These are usually set by a separate config file.
966             //
967             "user" : {
968             "name" : "",
969             "fullname" : "",
970             },
971              
972             // Instrument settings. These are usually set by a separate
973             // config file.
974             //
975             "instrument" : {
976             "type" : "",
977             "description" : "",
978             },
979              
980             // Note (chord root) names.
981             // Strings and tuning.
982             "tuning" : [ "E2", "A2", "D3", "G3", "B3", "E4" ],
983              
984             // In case of alternatives, the first one is used for output.
985             // Note that it is tempting to use real sharps and flats for output,
986             // but most fonts don't have the glyphs :(.
987             "notes" : {
988              
989             "system" : "common",
990              
991             "sharp" : [ "C", [ "C#", "Cis", "C♯" ],
992             "D", [ "D#", "Dis", "D♯" ],
993             "E",
994             "F", [ "F#", "Fis", "F♯" ],
995             "G", [ "G#", "Gis", "G♯" ],
996             "A", [ "A#", "Ais", "A♯" ],
997             "B",
998             ],
999              
1000             "flat" : [ "C",
1001             [ "Db", "Des", "Dâ™­" ], "D",
1002             [ "Eb", "Es", "Ees", "Eâ™­" ], "E",
1003             "F",
1004             [ "Gb", "Ges", "Gâ™­" ], "G",
1005             [ "Ab", "As", "Aes", "Aâ™­" ], "A",
1006             [ "Bb", "Bes", "Bâ™­" ], "B",
1007             ],
1008              
1009             // Movable means position independent (e.g. nashville).
1010             "movable" : false,
1011             },
1012              
1013             // User defined chords.
1014             // "base" defaults to 1.
1015             // Use 0 for an empty string, and -1 for a muted string.
1016             // "fingers" is optional.
1017             // "display" (optional) can be used to change the way the chord is displayed.
1018             "chords" : [
1019             // {
1020             // "name" : "Bb",
1021             // "base" : 1,
1022             // "frets" : [ 1, 1, 3, 3, 3, 1 ],
1023             // "fingers" : [ 1, 1, 2, 3, 4, 1 ],
1024             // "display" : "B\u266d",
1025             // },
1026             // If the name of the first entry is "defaults" its properties may
1027             // be used as defaults for the rest of the chords.
1028             ],
1029              
1030             // Printing chord diagrams.
1031             // "show": prints the chords used in the song.
1032             // "all": all chords used.
1033             // "user": only prints user defined chords.
1034             // "sorted": order the chords by key.
1035             // "suppress": a series of chord (names) thet will not generate
1036             // diagrams, e.g. if they are considered trivial.
1037             // Note: The type of diagram (string or keyboard) is determined
1038             // by the value of "instrument.type".
1039             "diagrams" : {
1040             "show" : "all",
1041             "sorted" : false,
1042             "suppress" : [],
1043             },
1044              
1045             // Diagnostic messages.
1046             "diagnostics" : {
1047             "format" : "\"%f\", line %n, %m\n\t%l",
1048             },
1049              
1050             // Table of contents.
1051             "contents" : [
1052             { "fields" : [ "songindex" ],
1053             "label" : "Table of Contents",
1054             "line" : "%{title}",
1055             "pageno" : "%{page}",
1056             "fold" : false,
1057             "omit" : false,
1058             },
1059             { "fields" : [ "sorttitle", "artist" ],
1060             "label" : "Contents by Title",
1061             "line" : "%{title}%{artist| - %{}}",
1062             "pageno" : "%{page}",
1063             "fold" : false,
1064             "omit" : false,
1065             },
1066             { "fields" : [ "artist", "sorttitle" ],
1067             "label" : "Contents by Artist",
1068             "line" : "%{artist|%{} - }%{title}",
1069             "pageno" : "%{page}",
1070             "fold" : false,
1071             "omit" : true,
1072             },
1073             ],
1074             // Table of contents, old style.
1075             // This will be ignored when new style contents is present.
1076             "toc" : {
1077             // Title for ToC.
1078             "title" : "Table of Contents",
1079             "line" : "%{title}",
1080             // Sorting order.
1081             // Currently only sorting by page number and alpha is implemented.
1082             "order" : "page",
1083             },
1084              
1085             // Delegates.
1086             // Basically a delegate is a section {start_of_XXX} which content is
1087             // collected and handled later by the backend.
1088              
1089             "delegates" : {
1090             "abc" : {
1091             "type" : "image",
1092             "module" : "ABC",
1093             "handler" : "abc2image",
1094             "config" : "default", // or "none", or "myformat.fmt"
1095             "preamble" : [],
1096             "preprocess" : { "abc" : [], "svg" : [] },
1097             },
1098             "ly" : {
1099             "type" : "image",
1100             "module" : "Lilypond",
1101             "handler" : "ly2image",
1102             "config" : "default", // or "none", or ...
1103             // The preamble is a list of lines inserted before the lilipond data.
1104             // This is a good place to set the version and global customizations.
1105             "preamble" : [
1106             "\\version \"2.21.0\"",
1107             "\\header { tagline = ##f }",
1108             ],
1109             },
1110             },
1111              
1112             // Definitions for PDF output.
1113              
1114             "pdf" : {
1115              
1116             // Choose a PDF::API2 compatible library, or leave empty to
1117             // have ChordPro choose one for you.
1118             "library" : "", // or "PDF::API2", or "PDF::Builder"
1119              
1120             // PDF Properties. Arbitrary key/values may be added.
1121             // Note that the context for substitutions is the first song.
1122             "info" : {
1123             "title" : "%{title}",
1124             "author" : "",
1125             "subject" : "",
1126             "keywords" : "",
1127             },
1128              
1129             // Papersize, 'a4' or [ 595, 842 ] etc.
1130             "papersize" : "a4",
1131              
1132             "theme" : {
1133             // Forgeround color. Usually black.
1134             "foreground" : "black",
1135             // Shades of grey.
1136             // medium is used for pressed keys in keyboard diagrams.
1137             "foreground-medium" : "grey70",
1138             // light is used as background for comments, cell bars, ...
1139             "foreground-light" : "grey90",
1140             // Background color. Usually none or white.
1141             "background" : "none",
1142             },
1143              
1144             // Space between columns, in pt.
1145             "columnspace" : 20,
1146              
1147             // Page margins.
1148             // Note that top/bottom exclude the head/footspace.
1149             "margintop" : 80,
1150             "marginbottom" : 40,
1151             "marginleft" : 40,
1152             "marginright" : 40,
1153             "headspace" : 60,
1154             "footspace" : 20,
1155              
1156             // Special: head on first page only, add the headspace to
1157             // the other pages so they become larger.
1158             "head-first-only" : false,
1159              
1160             // Spacings.
1161             // Baseline distances as a factor of the font size.
1162             "spacing" : {
1163             "title" : 1.2,
1164             "lyrics" : 1.2,
1165             "chords" : 1.2,
1166             "diagramchords" : 1.2,
1167             "grid" : 1.2,
1168             "tab" : 1.0,
1169             "toc" : 1.4,
1170             "empty" : 1.0,
1171             },
1172             // Note: By setting the font size and spacing for empty lines to
1173             // smaller values, you get a fine(r)-grained control over the
1174             // spacing between the various parts of the song.
1175              
1176             // Style of chorus.
1177             "chorus" : {
1178             "indent" : 0,
1179             // Chorus side bar.
1180             // Suppress by setting offset and/or width to zero.
1181             "bar" : {
1182             "offset" : 8,
1183             "width" : 1,
1184             "color" : "foreground",
1185             },
1186             "tag" : "Chorus",
1187             // Recall style: Print the tag using the type.
1188             // Alternatively quote the lines of the preceding chorus.
1189             "recall" : {
1190             "choruslike" : false,
1191             "tag" : "Chorus",
1192             "type" : "comment",
1193             "quote" : false,
1194             },
1195             },
1196              
1197             // This opens a margin for margin labels.
1198             "labels" : {
1199             // Margin width. Default is 0 (no margin labels).
1200             // "auto" will automatically reserve a margin if labels are used.
1201             "width" : "auto",
1202             // Alignment for the labels. Default is left.
1203             "align" : "left",
1204             // Alternatively, render labels as comments.
1205             "comment" : "" // "comment", "comment_italic" or "comment_box",
1206             },
1207              
1208             // Alternative songlines with chords in a side column.
1209             // Value is the column position.
1210             // "chordscolumn" : 400,
1211             "chordscolumn" : 0,
1212             "capoheading" : "%{capo|Capo: %{}}",
1213              
1214             // A {titles: left} may conflict with customized formats.
1215             // Set to non-zero to ignore the directive.
1216             "titles-directive-ignore" : false,
1217              
1218             // Chord diagrams.
1219             // A chord diagram consists of a number of cells.
1220             // Cell dimensions are specified by "width" and "height".
1221             // The horizontal number of cells depends on the number of strings.
1222             // The vertical number of cells is "vcells", which should
1223             // be 4 or larger to accomodate most chords.
1224             // The horizontal distance between diagrams is "hspace" cells.
1225             // The vertical distance is "vspace" cells.
1226             // "linewidth" is the thickness of the lines as a fraction of "width".
1227             // Diagrams for all chords of the song can be shown at the
1228             // "top", "bottom" or "right" side of the first page,
1229             // or "below" the last song line.
1230             "diagrams" : {
1231             "show" : "bottom",
1232             "width" : 6, // of a cell
1233             "height" : 6, // of a cell
1234             "vcells" : 4, // vertically
1235             "linewidth" : 0.1, // of a cell width
1236             "hspace" : 3.95, // fraction of width
1237             "vspace" : 3, // fraction of height
1238             "fingers" : true, // show fingering if available
1239             },
1240              
1241             // Keyboard diagrams.
1242             // A keyboard diagram consists of a number of keys.
1243             // Dimensions are specified by "width" (a key) and "height".
1244             // The horizontal distance between diagrams is "hspace" * keys * width.
1245             // The vertical distance is "vspace" * height.
1246             // "linewidth" is the thickness of the lines as a fraction of "width".
1247             // Diagrams for all chords of the song can be shown at the
1248             // "top", "bottom" or "right" side of the first page,
1249             // or "below" the last song line.
1250             "kbdiagrams" : {
1251             "show" : "bottom",
1252             "width" : 4, // of a single key
1253             "height" : 20, // of the diagram
1254             "keys" : 14, // or 7, 10, 14, 17, 21
1255             "base" : "C", // or "F"
1256             "linewidth" : 0.1, // fraction of a single key width
1257             "pressed" : "foreground-medium", // colour of a pressed key
1258             "hspace" : 3.95, // ??
1259             "vspace" : 0.3, // fraction of height
1260             },
1261              
1262             // Grid section lines.
1263             // Suppress when "show" is false, e.g. for singers.
1264             // The width and colour of the cell bar lines can be specified.
1265             // Enable by setting the width to the desired width.
1266             "grids" : {
1267             "cellbar" : {
1268             "width" : 0,
1269             "color" : "foreground-medium",
1270             },
1271             "show": true,
1272             "symbols" : {
1273             "color" : "blue",
1274             },
1275             "volta" : {
1276             "span" : 0.7,
1277             "color" : "blue",
1278             },
1279             },
1280              
1281             // Even/odd pages. A value of -1 denotes odd/even pages.
1282             "even-odd-pages" : 1,
1283             // Align songs to even/odd pages. When greater than 1, force alignment.
1284             "pagealign-songs" : 1,
1285             // PDF file to add as front matter.
1286             "front-matter" : "",
1287             // PDF file to add as back matter.
1288             "back-matter" : "",
1289              
1290             // Formats.
1291             // Pages have two title elements and one footer element. They also
1292             // can have a page of an existing PDF file as underlay (background).
1293             // Topmost is "title". It uses the "title" font as defined further below.
1294             // Second is "subtitle". It uses the "subtitle" font.
1295             // The "footer" uses the "footer" font.
1296             // All elements can have three fields, that are placed to the left side,
1297             // centered, and right side of the page.
1298             // The contents of all fields is defined below. You can use metadata
1299             // items in the fields as shown. By default, the "title" element shows the
1300             // value of metadata item "title", centered on the page. Likewise
1301             // "subtitle".
1302             // NOTE: The "title" and "subtitle" page elements have the same names
1303             // as the default metadata values which may be confusing. To show
1304             // metadata item, e.g. "artist", add its value to one of the
1305             // title/subtitle fields. Don't try to add an artist page element.
1306              
1307             "formats" : {
1308             // Titles/Footers.
1309              
1310             // Titles/footers have 3 parts, which are printed left,
1311             // centered and right.
1312             // For odd/even printing, the 1st background page is used
1313             // for left pages and the next page (if it exists) for right pages.
1314             // For even/odd printing, the order is reversed.
1315              
1316             // By default, a page has:
1317             "default" : {
1318             // No title/subtitle.
1319             "title" : [ "", "", "" ],
1320             "subtitle" : [ "", "", "" ],
1321             // Footer is title -- page number.
1322             "footer" : [ "%{title}", "", "%{page}" ],
1323             // Background page.
1324             "background" : "",
1325             },
1326             // The first page of a song has:
1327             "title" : {
1328             // Title and subtitle.
1329             "title" : [ "", "%{title}", "" ],
1330             "subtitle" : [ "", "%{subtitle}", "" ],
1331             // Footer with page number.
1332             "footer" : [ "", "", "%{page}" ],
1333             // Background page.
1334             "background" : "",
1335             },
1336             // The very first output page is slightly different:
1337             "first" : {
1338             // It has title and subtitle, like normal 'first' pages.
1339             // But no footer.
1340             "footer" : [ "", "", "" ],
1341             // Background page.
1342             "background" : "",
1343             },
1344             },
1345              
1346             // Split marker for syllables that are smaller than chord width.
1347             // split-marker is a 3-part array: 'start', 'repeat', and 'final'.
1348             // 'final' is always printed, last.
1349             // 'start' is printed if there is enough room.
1350             // 'repeat' is printed repeatedly to fill the rest.
1351             // If split-marker is a single string, this is 'start'.
1352             // All elements may be left empty strings.
1353             "split-marker" : [ "", "", "" ],
1354              
1355             // Font families and properties.
1356             // "fontconfig" maps members of font families to physical fonts.
1357             // Optionally, additional properties of the fonts can be specified.
1358             // Physical fonts can be the names of TrueType/OpenType fonts,
1359             // or names of built-in fonts (corefonts).
1360             // Relative filenames are looked up in the fontdir.
1361             // "fontdir" : [ "/usr/share/fonts/liberation", "/home/me/fonts" ],
1362              
1363             "fontdir" : [],
1364             "fontconfig" : {
1365             // alternatives: regular r normal
1366             // alternatives: bold b strong
1367             // alternatives: italic i oblique o emphasis
1368             // alternatives: bolditalic bi italicbold ib boldoblique bo obliquebold ob
1369             "times" : {
1370             "" : "Times-Roman",
1371             "bold" : "Times-Bold",
1372             "italic" : "Times-Italic",
1373             "bolditalic" : "Times-BoldItalic",
1374             },
1375             "helvetica" : {
1376             "" : "Helvetica",
1377             "bold" : "Helvetica-Bold",
1378             "oblique" : "Helvetica-Oblique",
1379             "boldoblique" : "Helvetica-BoldOblique",
1380             },
1381             "courier" : {
1382             "" : "Courier",
1383             "bold" : "Courier-Bold",
1384             "italic" : "Courier-Italic",
1385             "bolditalic" : "Courier-BoldItalic",
1386             },
1387             "dingbats" : {
1388             "" : "ZapfDingbats",
1389             },
1390             },
1391              
1392             // "fonts" maps output elements to fonts as defined in "fontconfig".
1393             // The elements can have a background colour associated.
1394             // Colours are "#RRGGBB" or predefined names like "black", "white",
1395             // and lots of others.
1396             // NOTE: In the built-in config we use only "name" since that can
1397             // be overruled with user settings.
1398              
1399             "fonts" : {
1400             "title" : {
1401             "name" : "Times-Bold",
1402             "size" : 14
1403             },
1404             "text" : {
1405             "name" : "Times-Roman",
1406             "size" : 12
1407             },
1408             "chord" : {
1409             "name" : "Helvetica-Oblique",
1410             "size" : 10
1411             },
1412             "chordfingers" : {
1413             "file" : "ChordProSymbols.ttf", // do not change
1414             "numbercolor" : "background",
1415             },
1416             "comment" : {
1417             "name" : "Helvetica",
1418             "size" : 12,
1419             "background" : "foreground-light"
1420             },
1421             "comment_italic" : {
1422             "name" : "Helvetica-Oblique",
1423             "size" : 12,
1424             },
1425             "comment_box" : {
1426             "name" : "Helvetica",
1427             "size" : 12,
1428             "frame" : 1
1429             },
1430             "tab" : {
1431             "name" : "Courier",
1432             "size" : 10
1433             },
1434             "toc" : {
1435             "name" : "Times-Roman",
1436             "size" : 11
1437             },
1438             "grid" : {
1439             "name" : "Helvetica",
1440             "size" : 10
1441             },
1442             },
1443              
1444             // Element mappings that can be specified, but need not since
1445             // they default to other elements.
1446             // subtitle --> text
1447             // comment --> text
1448             // comment_italic --> chord
1449             // comment_box --> chord
1450             // annotation --> chord
1451             // toc --> text
1452             // grid --> chord
1453             // grid_margin --> comment
1454             // footer --> subtitle @ 60%
1455             // empty --> text
1456             // diagram --> comment
1457             // diagram_base --> text (but at a small size)
1458              
1459             // Bookmarks (PDF outlines).
1460             // fields: primary and (optional) secondary fields.
1461             // label: outline label (omitted if there's only one outline)
1462             // line: text of the outline element
1463             // collapse: initial display is collapsed
1464             // letter: sublevel with first letters if more
1465             // fold: group by primary (NYI)
1466             // omit: ignore this
1467             "outlines" : [
1468             { "fields" : [ "sorttitle", "artist" ],
1469             "label" : "By Title",
1470             "line" : "%{title}%{artist| - %{}}",
1471             "collapse" : false,
1472             "letter" : 5,
1473             "fold" : false,
1474             },
1475             { "fields" : [ "artist", "sorttitle" ],
1476             "label" : "By Artist",
1477             "line" : "%{artist|%{} - }%{title}",
1478             "collapse" : false,
1479             "letter" : 5,
1480             "fold" : false,
1481             },
1482             ],
1483              
1484             // This will show the page layout if non-zero.
1485             "showlayout" : false,
1486              
1487             // CSV generation for MobileSheetsPro. Adapt for other tools.
1488             // Note that the resultant file will conform to RFC 4180.
1489             "csv" : {
1490             "fields" : [
1491             { "name" : "title", "meta" : "title" },
1492             { "name" : "pages", "meta" : "pagerange" },
1493             { "name" : "sort title", "meta" : "sorttitle" },
1494             { "name" : "artists", "meta" : "artist" },
1495             { "name" : "composers", "meta" : "composer" },
1496             { "name" : "collections", "meta" : "collection" },
1497             { "name" : "keys", "meta" : "key_actual" },
1498             { "name" : "years", "meta" : "year" },
1499             // Add "omit" : true to omit a field.
1500             // To add fields with fixed values, use "value":
1501             { "name" : "my_field", "value" : "text", "omit" : true },
1502             ],
1503             // Field separator.
1504             "separator" : ";",
1505             // Values separator.
1506             "vseparator" : "|",
1507             // Restrict CSV to song pages only (do not include matter pages).
1508             "songsonly" : true,
1509             },
1510             },
1511              
1512             // Settings for ChordPro backend.
1513             "chordpro" : {
1514             // Style of chorus.
1515             "chorus" : {
1516             // Recall style: Print the tag using the type.
1517             // Alternatively quote the lines of the preceding chorus.
1518             // If no tag+type or quote: use {chorus}.
1519             // Note: Variant 'msp' always uses {chorus}.
1520             "recall" : {
1521             // "tag" : "Chorus", "type" : "comment",
1522             "tag" : "", "type" : "",
1523             // "quote" : false,
1524             "quote" : false,
1525             },
1526             },
1527             },
1528              
1529             // Settings for HTML backend.
1530             "html" : {
1531             // Stylesheet links.
1532             "styles" : {
1533             "display" : "chordpro.css",
1534             "print" : "chordpro_print.css",
1535             },
1536             },
1537              
1538             // Settings for LaTeX backend.
1539             "latex" : {
1540             "template_include_path" : [ ],
1541             "templates" : {
1542             "songbook" : "songbook.tt",
1543             "comment" : "comment.tt",
1544             "image" : "image.tt"
1545             }
1546             },
1547              
1548             // Settings for Text backend.
1549             "text" : {
1550             // Style of chorus.
1551             "chorus" : {
1552             // Recall style: Print the tag using the type.
1553             // Alternatively quote the lines of the preceding chorus.
1554             // If no tag+type or quote: use {chorus}.
1555             // Note: Variant 'msp' always uses {chorus}.
1556             "recall" : {
1557             // "tag" : "Chorus", "type" : "comment",
1558             "tag" : "", "type" : "",
1559             // "quote" : false,
1560             "quote" : false,
1561             },
1562             },
1563             },
1564              
1565             // Settings for A2Crd.
1566             "a2crd" : {
1567             // Treat leading lyrics lines as title/subtitle lines.
1568             "infer-titles" : true,
1569             // Classification algorithm.
1570             "classifier" : "pct_chords",
1571             // Tab stop width for tab expansion. Set to zero to disable.
1572             "tabstop" : 8,
1573             },
1574              
1575             // Settings for the parser/preprocessor.
1576             // For selected lines, you can specify a series of
1577             // { "target" : "xxx", "replace" : "yyy" }
1578             // Every occurrence of "xxx" will be replaced by "yyy".
1579             // Use wisely.
1580             "parser" : {
1581             "preprocess" : {
1582             // All lines.
1583             "all" : [],
1584             // Directives.
1585             "directive" : [],
1586             // Song lines (lyrics) only.
1587             "songline" : [],
1588             },
1589             },
1590              
1591             // For (debugging (internal use only)).
1592             "debug" : {
1593             "chords" : 0,
1594             "config" : 0,
1595             "echo" : 0,
1596             "fonts" : 0,
1597             "images" : 0,
1598             "layout" : 0,
1599             "meta" : 0,
1600             "mma" : 0,
1601             "spacing" : 0,
1602             "song" : 0,
1603             "songfull" : 0,
1604             "csv" : 0,
1605             "abc" : 0,
1606             "ly" : 0,
1607             // For temporary use.
1608             "x1" : 0,
1609             "x2" : 0,
1610             "x3" : 0,
1611             },
1612              
1613             }
1614             // End of config.
1615             End_Of_Config
1616             }
1617              
1618             # For convenience.
1619              
1620 214     214 0 376 sub diagram_strings ( $self ) {
  214         402  
  214         363  
1621             # tuning is usually removed from the config.
1622             # scalar( @{ $self->{tuning} } );
1623 214         667 ChordPro::Chords::strings();
1624             }
1625              
1626 0     0 0   sub diagram_keys ( $self ) {
  0            
  0            
1627 0           $self->{kbdiagrams}->{keys};
1628             }
1629              
1630             # For debugging messages.
1631 0     0 0   sub qd ( $val, $compact = 0 ) {
  0            
  0            
  0            
1632 79     79   3228 use Data::Dumper qw();
  79         13087  
  79         27676  
1633 0           local $Data::Dumper::Sortkeys = 1;
1634 0           local $Data::Dumper::Indent = 1;
1635 0           local $Data::Dumper::Quotekeys = 0;
1636 0           local $Data::Dumper::Deparse = 1;
1637 0           local $Data::Dumper::Terse = 1;
1638 0           local $Data::Dumper::Trailingcomma = !$compact;
1639 0           local $Data::Dumper::Useperl = 1;
1640 0           local $Data::Dumper::Useqq = 0; # I want unicode visible
1641 0           my $x = Data::Dumper::Dumper($val);
1642 0 0         if ( $compact ) {
1643 0           $x =~ s/^bless\( (.*), '[\w:]+' \)$/$1/s;
1644 0           $x =~ s/\s+/ /gs;
1645             }
1646 0 0         defined wantarray ? $x : warn($x,"\n");
1647             }
1648              
1649             unless ( caller ) {
1650             binmode STDOUT => ':utf8';
1651             print( default_config() );
1652             exit;
1653             }
1654              
1655             1;
1656              
1657             =head1 DEFAULT CONFIGURATION
1658              
1659             The default configuration as built in. User and system
1660             configs go on top of this one.
1661              
1662             See L for
1663             extensive details and examples.
1664              
1665