File Coverage

blib/lib/ChordPro/Config.pm
Criterion Covered Total %
statement 405 564 71.8
branch 139 250 55.6
condition 71 153 46.4
subroutine 40 47 85.1
pod 0 23 0.0
total 655 1037 63.1


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   1542 use v5.26;
  79         296  
11 79     79   522 use utf8;
  79         201  
  79         709  
12 79     79   2013 use Carp;
  79         190  
  79         5999  
13 79     79   715 use feature qw( signatures );
  79         212  
  79         13417  
14 79     79   608 no warnings "experimental::signatures";
  79         204  
  79         3554  
15              
16 79     79   748 use App::Packager;
  79         208  
  79         564  
17 79     79   48746 use ChordPro;
  79         280  
  79         3464  
18 79     79   3829 use ChordPro::Utils;
  79         203  
  79         8368  
19 79     79   1536 use File::LoadLines;
  79         26015  
  79         3523  
20 79     79   534 use File::Spec;
  79         187  
  79         1667  
21 79     79   59502 use JSON::PP ();
  79         1231179  
  79         3224  
22 79     79   680 use Scalar::Util qw(reftype);
  79         235  
  79         5384  
23 79     79   603 use List::Util qw(any);
  79         184  
  79         5372  
24 79     79   47515 use Hash::Util;
  79         265051  
  79         623  
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 1055 sub configurator ( $opts = undef ) {
  197         1729  
  197         385  
51 197         2961 my $pp = JSON::PP->new->relaxed;
52              
53             # Test programs call configurator without options.
54             # Prepare a minimal config.
55 197 100       11959 unless ( $opts ) {
56 78         1531 my $cfg = $pp->decode( default_config() );
57 78         9169189 $config = $cfg;
58 78         463 $options = { verbose => 0 };
59 78         572 process_config( $cfg, "" );
60 78         486 $cfg->{settings}->{lineinfo} = 0;
61 78         1916 return $cfg;
62             }
63 119 100       633 if ( keys(%$opts) ) {
64 9   50     25 $options = { %{$options//{}}, %$opts };
  9         76  
65             }
66              
67 119         343 my @cfg;
68 119   100     806 my $verbose = $options->{verbose} //= 0;
69              
70             # Load defaults.
71 119 50       521 warn("Reading: \n") if $verbose > 1;
72 119         557 my $cfg = $pp->decode( default_config() );
73              
74             # Default first.
75 119         13951256 @cfg = prep_configs( $cfg, "" );
76             # Bubble default config to be the first.
77 119 50       796 unshift( @cfg, pop(@cfg) ) if @cfg > 1;
78              
79             # Collect other config files.
80             my $add_config = sub {
81 11     11   41 my $fn = shift;
82 11         46 $cfg = get_config( $fn );
83 11         80 push( @cfg, prep_configs( $cfg, $fn ) );
84 119         1089 };
85              
86 119         481 foreach my $c ( qw( sysconfig userconfig config ) ) {
87 357 100       1630 next if $options->{"no$c"};
88 9 100       105 if ( ref($options->{$c}) eq 'ARRAY' ) {
89 2         9 $add_config->($_) foreach @{ $options->{$c} };
  2         14  
90             }
91             else {
92 7 50       33 warn("Adding config for $c\n") if $verbose;
93 7         34 $add_config->( $options->{$c} );
94             }
95             }
96              
97             # Now we have a list of all config files. Weed out dups.
98 119         723 for ( my $a = 0; $a < @cfg; $a++ ) {
99 249 50 66     1845 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       1356 print STDERR ("Config[$a]: ", $cfg[$a]->{_src}, "\n" )
104             if $verbose;
105             }
106              
107 119         372 $cfg = shift(@cfg);
108 119 50       568 warn("Process: $cfg->{_src}\n") if $verbose > 1;
109              
110             # Presets.
111 119 50       594 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     53412 || getlogin() || getpwuid($<) || "chordpro" );
119 119   50     660 $cfg->{user}->{fullname} = eval { (getpwuid($<))[6] } || "";
120             }
121              
122             # Add some extra entries to prevent warnings.
123 119         1010 for ( qw(title subtitle footer) ) {
124 357 100       1638 next if exists($cfg->{pdf}->{formats}->{first}->{$_});
125 238         932 $cfg->{pdf}->{formats}->{first}->{$_} = "";
126             }
127 119         879 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         3555 for ( qw(name file description size color background) ) {
133 12852   100     42213 $cfg->{pdf}->{fonts}->{$ff}->{$_} //= undef;
134             }
135             }
136              
137             my $backend_configurator =
138 119         2311 UNIVERSAL::can( $options->{backend}, "configurator" );
139              
140             # Apply config files
141 119         565 foreach my $new ( @cfg ) {
142 130         460 my $file = $new->{_src}; # for diagnostics
143             # Handle obsolete keys.
144 130 50       594 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       957 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         421 local $::config = $cfg;
157 130         715 process_config( $new, $file );
158             # Merge final.
159 130         777 $cfg = hmerge( $cfg, $new );
160             }
161              
162             # Handle defines from the command line.
163 119         1130 $cfg = hmerge( $cfg, prp2cfg( $options->{define}, $cfg ) );
164              
165             # Sanitize added extra entries.
166 119         879 for ( qw(title subtitle footer) ) {
167             delete($cfg->{pdf}->{formats}->{first}->{$_})
168 357 100 50     2256 if ($cfg->{pdf}->{formats}->{first}->{$_} // 1) eq "";
169 357         797 for my $class ( qw(title first default) ) {
170 1071         2177 my $t = $cfg->{pdf}->{formats}->{$class}->{$_};
171 1071 100       2178 next unless $t;
172 833 50       1902 die("Config error in pdf.formats.$class.$_: not an array\n")
173             unless ref($t) eq 'ARRAY';
174 833 50       2221 if ( ref($t->[0]) ne 'ARRAY' ) {
175 833         1458 $t = [ $t ];
176             }
177 833         1409 my $tt = $_;
178 833         1453 for ( @$t) {
179 833 50 33     3594 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       683 if ( $cfg->{pdf}->{fontdir} ) {
187 119         366 my @a;
188 119 50       610 if ( ref($cfg->{pdf}->{fontdir}) eq 'ARRAY' ) {
189 119         296 @a = @{ $cfg->{pdf}->{fontdir} };
  119         477  
190             }
191             else {
192 0         0 @a = ( $cfg->{pdf}->{fontdir} );
193             }
194 119         410 $cfg->{pdf}->{fontdir} = [];
195 119 50       1194 my $split = $^O =~ /^MS*/ ? qr(;) : qr(:);
196 119         647 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         313 my @allfonts = keys(%{$cfg->{pdf}->{fonts}});
  119         1403  
206 119         451 for my $ff ( @allfonts ) {
207 2142 100 66     9116 unless ( $cfg->{pdf}->{fonts}->{$ff}->{name}
      66        
208             || $cfg->{pdf}->{fonts}->{$ff}->{description}
209             || $cfg->{pdf}->{fonts}->{$ff}->{file} ) {
210 952         2311 delete( $cfg->{pdf}->{fonts}->{$ff} );
211 952         1619 next;
212             }
213 1190   50     5150 $cfg->{pdf}->{fonts}->{$ff}->{color} //= "foreground";
214 1190   100     4681 $cfg->{pdf}->{fonts}->{$ff}->{background} //= "background";
215 1190         2556 for ( qw(name file description size) ) {
216             delete( $cfg->{pdf}->{fonts}->{$ff}->{$_} )
217 4760 100       11381 unless defined( $cfg->{pdf}->{fonts}->{$ff}->{$_} );
218             }
219             }
220              
221 119 50       1319 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         419 for ( qw( transpose transcode decapo lyrics-only strict ) ) {
237 595 100       1642 next unless defined $options->{$_};
238 18         78 $cfg->{settings}->{$_} = $options->{$_};
239             }
240              
241 119         555 for ( "front-matter", "back-matter" ) {
242 238 100       855 next unless defined $options->{$_};
243 6         27 $cfg->{pdf}->{$_} = $options->{$_};
244             }
245              
246 119 50       595 if ( defined $options->{'chord-grids-sorted'} ) {
247 0         0 $cfg->{diagrams}->{sorted} = $options->{'chord-grids-sorted'};
248             }
249              
250             # For convenience...
251 119         838 bless( $cfg, __PACKAGE__ );
252              
253 119 50       494 return $cfg if $options->{'cfg-print'};
254              
255             # Backend specific configs.
256 119 100       519 $backend_configurator->($cfg) if $backend_configurator;
257              
258             # Locking the hash is mainly for development.
259 119         782 $cfg->lock;
260              
261 119 50       233662 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         46300 return $cfg;
272             }
273              
274             # Get the decoded contents of a single config file.
275 132     132 0 462 sub get_config ( $file ) {
  132         335  
  132         309  
276 132 50       542 Carp::confess("FATAL: Undefined config") unless defined $file;
277 132         515 my $verbose = $options->{verbose};
278 132 50       689 warn("Reading: $file\n") if $verbose > 1;
279 132         630 $file = expand_tilde($file);
280              
281 132 50       1254 if ( $file =~ /\.json$/i ) {
    0          
282 132 50       10653 if ( open( my $fd, "<:raw", $file ) ) {
283 132         2143 my $pp = JSON::PP->new->relaxed;
284 132         10268 my $new = $pp->decode( loadlines( $fd, { split => 0 } ) );
285 132         32440018 precheck( $new, $file );
286 132         4663 close($fd);
287 132         3290 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 745 sub prep_configs ( $cfg, $src ) {
  249         735  
  249         726  
  249         588  
311 249         1104 $cfg->{_src} = $src;
312              
313 249         812 my @res;
314              
315             # If there are includes, add them first.
316 249         8290 my ( $vol, $dir, undef ) = File::Spec->splitpath($cfg->{_src});
317 249         888 foreach my $c ( @{ $cfg->{include} } ) {
  249         1319  
318             # Check for resource names.
319 119 50 0     743 if ( $c !~ m;[/.]; ) {
    0          
320 119         1316 $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         769 my $cfg = get_config($c);
328             # Recurse.
329 119         938 push( @res, prep_configs( $cfg, $c ) );
330             }
331              
332             # Push this and return.
333 249         754 push( @res, $cfg );
334 249         1281 return @res;
335             }
336              
337 208     208 0 624 sub process_config ( $cfg, $file ) {
  208         492  
  208         543  
  208         422  
338 208         663 my $verbose = $options->{verbose};
339              
340 208 50       969 warn("Process: $file\n") if $verbose > 1;
341              
342 208 100       1091 if ( $cfg->{tuning} ) {
343 197         1615 my $res =
344             ChordPro::Chords::set_tuning( $cfg );
345 197 50       815 warn( "Invalid tuning in config: ", $res, "\n" ) if $res;
346 197         816 $cfg->{_tuning} = $cfg->{tuning};
347 197         628 $cfg->{tuning} = [];
348             }
349              
350 208         1235 ChordPro::Chords::reset_parser;
351 208         2798 ChordPro::Chords::Parser->reset_parsers;
352 208         1045 local $::config = hmerge( $::config, $cfg );
353 208 100       1554 if ( $cfg->{chords} ) {
354 197         2084 ChordPro::Chords::push_parser($cfg->{notes}->{system});
355 197         748 my $c = $cfg->{chords};
356 197 50 66     1528 if ( @$c && $c->[0] eq "append" ) {
357 0         0 shift(@$c);
358             }
359 197         913 foreach ( @$c ) {
360 49742         112922 my $res =
361             ChordPro::Chords::add_config_chord($_);
362             warn( "Invalid chord in config: ",
363 49742 50       130191 $_->{name}, ": ", $res, "\n" ) if $res;
364             }
365 197 50       1294 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         990 $cfg->{_chords} = delete $cfg->{chords};
371 197         1040 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 677 sub lock ( $self ) {
  302         599  
  302         595  
451 302         1501 Hash::Util::lock_hashref_recurse($self);
452             }
453              
454 183     183 0 422 sub unlock ( $self ) {
  183         397  
  183         315  
455 183         813 Hash::Util::unlock_hashref_recurse($self);
456             }
457              
458 16     16 0 35 sub is_locked ( $self ) {
  16         20  
  16         37  
459 16         84 Hash::Util::hashref_locked($self);
460             }
461              
462             # Augment / Reduce.
463              
464 15     15 0 1801 sub augment ( $self, $hash ) {
  15         29  
  15         31  
  15         23  
465              
466 15         61 my $locked = $self->is_locked;
467 15 100       156 $self->unlock if $locked;
468              
469 15         23064 $self->_augment( $hash, "" );
470              
471 15 100       75 $self->lock if $locked;
472              
473 15         25087 $self;
474             }
475              
476              
477 43     43   70 sub _augment ( $self, $hash, $path ) {
  43         71  
  43         64  
  43         66  
  43         62  
478              
479 43         210 for my $key ( keys(%$hash) ) {
480              
481             warn("Config augment error: unknown item $path$key\n")
482 45 0 33     204 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     239 if ( ref($hash->{$key}) eq 'HASH' ) {
    100          
490 28 50       81 if ( ref($self->{$key}) eq 'HASH' ) {
    0          
491              
492             # Hashes. Recurse.
493 28         153 _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       3 if ( @{$hash->{$key}} ) {
  2         6  
516 2         4 my @v = @{ $hash->{$key} };
  2         15  
517 2 50       11 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         2 shift(@v);
524             # Prepend the rest.
525 1         2 unshift( @{ $self->{$key} }, @v );
  1         5  
526             }
527             else {
528             # Overwrite.
529 1         4 $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         55 $self->{$key} = $hash->{$key};
541             }
542             }
543              
544 43         87 $self;
545             }
546              
547 79     79   463140 use constant DEBUG => 0;
  79         1554  
  79         307508  
548              
549 1     1 0 1647 sub reduce ( $self, $hash ) {
  1         3  
  1         2  
  1         2  
550              
551 1         3 my $locked = $self->is_locked;
552              
553 1         6 warn("O: ", qd($hash,1), "\n") if DEBUG;
554 1         2 warn("N: ", qd($self,1), "\n") if DEBUG;
555 1         4 my $state = _reduce( $self, $hash, "" );
556              
557 1 50       11 $self->lock if $locked;
558              
559 1         4 warn("== ", qd($self,1), "\n") if DEBUG;
560 1         4 return $self;
561             }
562              
563 68     68   89 sub _ref ( $self ) {
  68         131  
  68         84  
564 68   66     319 reftype($self) // ref($self);
565             }
566              
567 6     6   8 sub _reduce ( $self, $orig, $path ) {
  6         9  
  6         8  
  6         11  
  6         10  
568              
569 6         8 my $state;
570              
571 6 100 66     12 if ( _ref($self) eq 'HASH' && _ref($orig) eq 'HASH' ) {
572              
573 3         13 warn("D: ", qd($self,1), "\n") if DEBUG && !%$orig;
574 3 50       12 return 'D' unless %$orig;
575              
576 3         11 my %hh = map { $_ => 1 } keys(%$self), keys(%$orig);
  12         27  
577 3         14 for my $key ( sort keys(%hh) ) {
578              
579             warn("Config reduce error: unknown item $path$key\n")
580 6 50 33     17 unless exists $self->{$key}
581             || $key =~ /^_/;
582              
583 6 50       15 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     12 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         22 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     18 $state //= 'M' if $m ne 'I';
600             }
601              
602             elsif ( ($self->{$key}//'') eq ($orig->{$key}//'') ) {
603 1         2 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         3 warn("M: $path$key => $self->{$key}\n") if DEBUG;
620 1   50     6 $state //= 'M';
621             }
622             }
623 3   50     11 return $state // 'I';
624             }
625              
626 3 50 33     8 if ( _ref($self) eq 'ARRAY' && _ref($orig) eq 'ARRAY' ) {
627              
628             # Arrays.
629 3 100   5   18 if ( any { _ref($_) } @$self ) {
  5         9  
630             # Complex arrays. Recurse.
631 1         7 for ( my $key = 0; $key < @$self; $key++ ) {
632 1         9 my $m = _reduce( $self->[$key], $orig->[$key], "$path$key." );
633             #delete $self->{$key} if $m eq 'D'; # TODO
634 1 50 50     8 $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       12 if ( my $dd = @$self - @$orig ) {
641 1         6 $path =~ s/\.$//;
642 1 50       10 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         5 for ( my $ix = 0; $ix < @$orig; $ix++ ) {
647 1 50       4 next if $orig->[$ix] eq $self->[$ix];
648 1         2 $t++;
649 1         3 last;
650             }
651 1 50       3 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         3 undef $t;
657 1         5 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       12 unless ( $t ) {
663 1         3 warn("M: $path prepend @{$self}[0..$dd-1]\n") if DEBUG;
664 1         4 splice( @$self, $dd );
665 1         5 unshift( @$self, "prepend" );
666 1         5 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         2 my $t;
680 1         4 for ( my $ix = 0; $ix < @$orig; $ix++ ) {
681 1 50       4 next if $orig->[$ix] eq $self->[$ix];
682 1         2 warn("M: $path$ix => $self->[$ix]\n") if DEBUG;
683 1         1 $t++;
684 1         10 last;
685             }
686 1 50       5 if ( $t ) {
687 1         2 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 9419 sub hmerge( $left, $right, $path = "" ) {
  6938         9347  
  6938         9031  
  6938         9971  
  6938         9009  
706              
707             # Merge hashes. Right takes precedence.
708             # Based on Hash::Merge::Simple by Robert Krimen.
709              
710 6938         51934 my %res = %$left;
711              
712 6938         18718 for my $key ( keys(%$right) ) {
713              
714             warn("Config error: unknown item $path$key\n")
715 25129 50 66     53453 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     77193 if ( ref($right->{$key}) eq 'HASH'
    100 100        
724             and
725             ref($res{$key}) eq 'HASH' ) {
726              
727             # Hashes. Recurse.
728 6479         19210 $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         4475 join(" ", map { qq{"$_"} } @{ $right->{$key} }),
737             " \n") if 0;
738             # Arrays. Overwrite or append.
739 2849 100       3938 if ( @{$right->{$key}} ) {
  2849         6185  
740 1493         2190 my @v = @{ $right->{$key} };
  1493         8871  
741 1493 50       4489 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         4596 $res{$key} = $right->{$key};
762             }
763             }
764             else {
765             # Overwrite.
766 1356         2868 $res{$key} = $right->{$key};
767             }
768             }
769             else {
770             # Overwrite.
771 15801         25959 $res{$key} = $right->{$key};
772             }
773             }
774              
775 6938         19025 return \%res;
776             }
777              
778 29865     29865 0 39294 sub clone ( $source ) {
  29865         38990  
  29865         35855  
779              
780 29865 50       52367 return if not defined($source);
781 29865         43924 my $ref_type = ref($source);
782              
783             # Non-reference values are copied as is.
784 29865 50       48125 return $source unless $ref_type;
785              
786             # Ignore blessed objects unless it's me.
787 29865         36828 my $class;
788 29865 100       242883 if ( "$source" =~ /^\Q$ref_type\E\=(\w+)\(0x[0-9a-f]+\)$/ ) {
789 40         143 $class = $ref_type;
790 40         140 $ref_type = $1;
791 40 0 33     204 return unless $class eq __PACKAGE__
      33        
792             || $source->can('is_chord') # chord info object
793             || $source->can('have_parser') # parser object
794             ;
795             }
796              
797 29865         70389 my $copy;
798 29865 100 33     56696 if ( $ref_type eq 'HASH' ) {
    100          
    50          
799 20200         32075 $copy = {};
800 20200 100       51024 my @a = map { ref($_) ? clone($_) : $_ } %$source;
  96960         188549  
801 20200 50       44853 ::dump(\@a) if @a % 2 == 1;
802 20200         56616 %$copy = @a;
803             }
804             elsif ( $ref_type eq 'ARRAY' ) {
805 8120         13641 $copy = [];
806 8120 100       14230 @$copy = map { ref($_) ? clone($_) : $_ } @$source;
  59760         113034  
807             }
808             elsif ( $ref_type eq 'REF' or $ref_type eq 'SCALAR' ) {
809 0         0 $copy = \( my $var = "" );
810 0         0 $$copy = clone($$source);
811             }
812             else {
813             # Plain copy anything else.
814 1545         2329 $copy = $source;
815             }
816 29865 100       55892 bless( $copy, $class ) if $class;
817 29865         60292 return $copy;
818             }
819              
820 132     132 0 544 sub precheck ( $cfg, $file ) {
  132         412  
  132         381  
  132         302  
821              
822 132         620 my $verbose = $options->{verbose};
823 132 50       709 warn("Verify config \"$file\"\n") if $verbose > 1;
824 132         376 my $p;
825             $p = sub {
826 283271     283271   470673 my ( $o, $path ) = @_;
827 283271   100     452571 $path //= "";
828 283271 50       993786 if ( !defined $o ) {
    100          
    100          
829 0         0 warn("$file: Undefined config \"$path\" (using 'false')\n");
830 0         0 $_[0] = '';
831             }
832             elsif ( UNIVERSAL::isa( $o, 'HASH' ) ) {
833 50244 100       94187 $path .= "." unless $path eq "";
834 50244         201481 for ( sort keys %$o ) {
835 119293         257146 $p->( $o->{$_}, $path . $_ );
836             }
837             }
838             elsif ( UNIVERSAL::isa( $o, 'ARRAY' ) ) {
839 19175 50       37380 $path .= "." unless $path eq "";
840 19175         40304 for ( my $i = 0; $i < @$o; $i++ ) {
841 163846         366169 $p->( $o->[$i], $path . "$i" );
842             }
843             }
844 132         1969 };
845              
846 132         594 $p->($cfg);
847             }
848              
849              
850             ## Data::Properties compatible API.
851             #
852             # Note: Lookup always takes the context into account.
853             # Note: Always signals undefined values.
854              
855             my $prp_context = "";
856              
857 12     12 0 19 sub get_property ( $p, $prp, $def = undef ) {
  12         20  
  12         17  
  12         19  
  12         16  
858 12 100       65 for ( split( /\./,
859             $prp_context eq ""
860             ? $prp
861             : "$prp_context.$prp" ) ) {
862 34 100       97 if ( /^\d+$/ ) {
863 5 50       17 die("No config $prp\n") unless _ref($p) eq 'ARRAY';
864 5         16 $p = $p->[$_];
865             }
866             else {
867 29 50       52 die("No config $prp\n") unless _ref($p) eq 'HASH';
868 29         67 $p = $p->{$_};
869             }
870             }
871 12   66     43 $p //= $def;
872 12 50       25 die("No config $prp\n") unless defined $p;
873 12         74 $p;
874             }
875              
876             *gps = \&get_property;
877              
878             sub set_property {
879 0     0 0 0 ...;
880             }
881              
882 2     2 0 5 sub set_context ( $self, $ctx = "" ) {
  2         4  
  2         5  
  2         3  
883 2         6 $prp_context = $ctx;
884             }
885              
886 0     0 0 0 sub get_context () {
  0         0  
887 0         0 $prp_context;
888             }
889              
890             # For testing
891 79     79   819 use base qw(Exporter);
  79         200  
  79         51273  
892             our @EXPORT = qw( _c );
893 12     12   1884 sub _c ( @args ) { $::config->gps(@args) }
  12         25  
  12         19  
  12         38  
894              
895             # Get the raw contents of the builtin (default) config.
896 197     197 0 3025 sub default_config () {
  197         1642  
897 197         3525 return <<'End_Of_Config';
898             // Configuration for ChordPro.
899             //
900             // This is a relaxed JSON document, so comments are possible.
901              
902             {
903             // Includes. These are processed first, before the rest of
904             // the config file.
905             //
906             // "include" takes a list of either filenames or preset names.
907             // "include" : [ "modern1", "lib/mycfg.json" ],
908             "include" : [ "guitar" ],
909              
910             // General settings, often changed by configs and command line.
911             "settings" : {
912             // Strict behaviour.
913             "strict" : true,
914             // Obsolete -- lineinfo is always included.
915             "lineinfo" : true,
916             // Titles flush: default center.
917             "titles" : "center",
918             // Columns, default one.
919             "columns" : 1,
920             // Suppress empty chord lines.
921             // Overrides the -a (--single-space) command line options.
922             "suppress-empty-chords" : true,
923             // Suppress blank lyrics lines.
924             "suppress-empty-lyrics" : true,
925             // Suppress chords.
926             // Overrides --lyrics-only command line option.
927             "lyrics-only" : false,
928             // Memorize chords in sections, to be recalled by [^].
929             "memorize" : false,
930             // Format to show chord names. May contain markup.
931             // "chord-format" : "%{root}%{qual|%{}}%{ext|%{}}%{bass|/%{}}",
932             "chord-format" : "%{root|%{}%{qual|%{}}%{ext|%{}}%{bass|/%{}}|%{name}}",
933             // Chords inline.
934             // May be a string containing pretext %s posttext.
935             // Defaults to "[%s]" if set to a value that doesn't contain "%s".
936             "inline-chords" : false,
937             // Same, for annotations. Ignored unless inline-chords is set.
938             // Must be a string containing pretext %s posttext.
939             // Default is "%s".
940             "inline-annotations" : "%s",
941             // Chords under the lyrics.
942             "chords-under" : false,
943             // Transposing.
944             "transpose" : 0,
945             // Transcoding.
946             "transcode" : "",
947             // Always decapoize.
948             "decapo" : false,
949             // Chords parsing strategy.
950             // Strict (only known) or relaxed (anything that looks sane).
951             "chordnames": "strict",
952             // Allow note names in [].
953             "notenames" : false,
954             // Always replace chords by their canonical form.
955             "chords-canonical" : false,
956             // If false, chorus labels are used as tags.
957             "choruslabels" : true,
958             // Substitute Unicode sharp/flats in chord names.
959             // Will fallback to ChordProSymbols the font doesn't have the glyphs.
960             "truesf" : false,
961             },
962              
963             // Metadata.
964             // For these keys you can use {meta key ...} as well as {key ...}.
965             // If strict is nonzero, only the keys named here are allowed.
966             // If strict is zero, {meta ...} will accept any key.
967             // Important: "title" and "subtitle" must always be in this list.
968             // The separator is used to concatenate multiple values.
969             // If autosplit is true, the separator is also used to split
970             // values upon input.
971             "metadata" : {
972             "keys" : [ "title", "subtitle",
973             "artist", "composer", "lyricist", "arranger",
974             "album", "copyright", "year",
975             "sorttitle",
976             "key", "time", "tempo", "capo", "duration" ],
977             "strict" : true,
978             "separator" : "; ",
979             "autosplit" : true,
980             },
981             // Globally defined (added) meta data,
982             // This is explicitly NOT intended for the metadata items above.
983             "meta" : {
984             },
985              
986             // Assets.
987             "assets" : {},
988              
989             // Dates. Format is a strftime template.
990             "dates" : {
991             "today" : {
992             "format" : "%A, %B %e, %Y"
993             }
994             },
995              
996             // User settings. These are usually set by a separate config file.
997             //
998             "user" : {
999             "name" : "",
1000             "fullname" : "",
1001             },
1002              
1003             // Instrument settings. These are usually set by a separate
1004             // config file.
1005             //
1006             "instrument" : {
1007             "type" : "",
1008             "description" : "",
1009             },
1010              
1011             // Note (chord root) names.
1012             // Strings and tuning.
1013             "tuning" : [ "E2", "A2", "D3", "G3", "B3", "E4" ],
1014              
1015             // In case of alternatives, the first one is used for output.
1016             // Note that it is tempting to use real sharps and flats for output,
1017             // but most fonts don't have the glyphs :(.
1018             "notes" : {
1019              
1020             "system" : "common",
1021              
1022             "sharp" : [ "C", [ "C#", "Cis", "C♯" ],
1023             "D", [ "D#", "Dis", "D♯" ],
1024             "E",
1025             "F", [ "F#", "Fis", "F♯" ],
1026             "G", [ "G#", "Gis", "G♯" ],
1027             "A", [ "A#", "Ais", "A♯" ],
1028             "B",
1029             ],
1030              
1031             "flat" : [ "C",
1032             [ "Db", "Des", "Dâ™­" ], "D",
1033             [ "Eb", "Es", "Ees", "Eâ™­" ], "E",
1034             "F",
1035             [ "Gb", "Ges", "Gâ™­" ], "G",
1036             [ "Ab", "As", "Aes", "Aâ™­" ], "A",
1037             [ "Bb", "Bes", "Bâ™­" ], "B",
1038             ],
1039              
1040             // Movable means position independent (e.g. nashville).
1041             "movable" : false,
1042             },
1043              
1044             // User defined chords.
1045             // "base" defaults to 1.
1046             // Use 0 for an empty string, and -1 for a muted string.
1047             // "fingers" is optional.
1048             // "display" (optional) can be used to change the way the chord is displayed.
1049             "chords" : [
1050             // {
1051             // "name" : "Bb",
1052             // "base" : 1,
1053             // "frets" : [ 1, 1, 3, 3, 3, 1 ],
1054             // "fingers" : [ 1, 1, 2, 3, 4, 1 ],
1055             // "display" : "B\u266d",
1056             // },
1057             // If the name of the first entry is "defaults" its properties may
1058             // be used as defaults for the rest of the chords.
1059             ],
1060              
1061             // Printing chord diagrams.
1062             // "show": prints the chords used in the song.
1063             // "all": all chords used.
1064             // "user": only prints user defined chords.
1065             // "sorted": order the chords by key.
1066             // "suppress": a series of chord (names) thet will not generate
1067             // diagrams, e.g. if they are considered trivial.
1068             // Note: The type of diagram (string or keyboard) is determined
1069             // by the value of "instrument.type".
1070             "diagrams" : {
1071             "show" : "all",
1072             "sorted" : false,
1073             "suppress" : [],
1074             },
1075              
1076             // Diagnostic messages.
1077             "diagnostics" : {
1078             "format" : "\"%f\", line %n, %m\n\t%l",
1079             },
1080              
1081             // Table of contents.
1082             "contents" : [
1083             { "fields" : [ "songindex" ],
1084             "label" : "Table of Contents",
1085             "line" : "%{title}",
1086             "pageno" : "%{page}",
1087             "fold" : false,
1088             "omit" : false,
1089             },
1090             { "fields" : [ "sorttitle", "artist" ],
1091             "label" : "Contents by Title",
1092             "line" : "%{title}%{artist| - %{}}",
1093             "pageno" : "%{page}",
1094             "fold" : false,
1095             "omit" : false,
1096             },
1097             { "fields" : [ "artist", "sorttitle" ],
1098             "label" : "Contents by Artist",
1099             "line" : "%{artist|%{} - }%{title}",
1100             "pageno" : "%{page}",
1101             "fold" : false,
1102             "omit" : true,
1103             },
1104             ],
1105             // Table of contents, old style.
1106             // This will be ignored when new style contents is present.
1107             "toc" : {
1108             // Title for ToC.
1109             "title" : "Table of Contents",
1110             "line" : "%{title}",
1111             // Sorting order.
1112             // Currently only sorting by page number and alpha is implemented.
1113             "order" : "page",
1114             },
1115              
1116             // Delegates.
1117             // Basically a delegate is a section {start_of_XXX} which content is
1118             // collected and handled later by the backend.
1119              
1120             "delegates" : {
1121             "abc" : {
1122             "type" : "image",
1123             "module" : "ABC",
1124             "handler" : "abc2image",
1125             "config" : "default", // or "none", or "myformat.fmt"
1126             "preamble" : [],
1127             "preprocess" : { "abc" : [], "svg" : [] },
1128             },
1129             "ly" : {
1130             "type" : "image",
1131             "module" : "Lilypond",
1132             "handler" : "ly2image",
1133             "config" : "default", // or "none", or ...
1134             // The preamble is a list of lines inserted before the lilipond data.
1135             // This is a good place to set the version and global customizations.
1136             "preamble" : [
1137             "\\version \"2.21.0\"",
1138             "\\header { tagline = ##f }",
1139             ],
1140             },
1141             },
1142              
1143             // Definitions for PDF output.
1144              
1145             "pdf" : {
1146              
1147             // Choose a PDF::API2 compatible library, or leave empty to
1148             // have ChordPro choose one for you.
1149             "library" : "", // or "PDF::API2", or "PDF::Builder"
1150              
1151             // PDF Properties. Arbitrary key/values may be added.
1152             // Note that the context for substitutions is the first song.
1153             "info" : {
1154             "title" : "%{title}",
1155             "author" : "",
1156             "subject" : "",
1157             "keywords" : "",
1158             },
1159              
1160             // Papersize, 'a4' or [ 595, 842 ] etc.
1161             "papersize" : "a4",
1162              
1163             "theme" : {
1164             // Forgeround color. Usually black.
1165             "foreground" : "black",
1166             // Shades of grey.
1167             // medium is used for pressed keys in keyboard diagrams.
1168             "foreground-medium" : "grey70",
1169             // light is used as background for comments, cell bars, ...
1170             "foreground-light" : "grey90",
1171             // Background color. Usually none or white.
1172             "background" : "none",
1173             },
1174              
1175             // Space between columns, in pt.
1176             "columnspace" : 20,
1177              
1178             // Page margins.
1179             // Note that top/bottom exclude the head/footspace.
1180             "margintop" : 80,
1181             "marginbottom" : 40,
1182             "marginleft" : 40,
1183             "marginright" : 40,
1184             "headspace" : 60,
1185             "footspace" : 20,
1186              
1187             // Special: head on first page only, add the headspace to
1188             // the other pages so they become larger.
1189             "head-first-only" : false,
1190              
1191             // Spacings.
1192             // Baseline distances as a factor of the font size.
1193             "spacing" : {
1194             "title" : 1.2,
1195             "lyrics" : 1.2,
1196             "chords" : 1.2,
1197             "diagramchords" : 1.2,
1198             "grid" : 1.2,
1199             "tab" : 1.0,
1200             "toc" : 1.4,
1201             "empty" : 1.0,
1202             },
1203             // Note: By setting the font size and spacing for empty lines to
1204             // smaller values, you get a fine(r)-grained control over the
1205             // spacing between the various parts of the song.
1206              
1207             // Style of chorus.
1208             "chorus" : {
1209             "indent" : 0,
1210             // Chorus side bar.
1211             // Suppress by setting offset and/or width to zero.
1212             "bar" : {
1213             "offset" : 8,
1214             "width" : 1,
1215             "color" : "foreground",
1216             },
1217             "tag" : "Chorus",
1218             // Recall style: Print the tag using the type.
1219             // Alternatively quote the lines of the preceding chorus.
1220             "recall" : {
1221             "choruslike" : false,
1222             "tag" : "Chorus",
1223             "type" : "comment",
1224             "quote" : false,
1225             },
1226             },
1227              
1228             // This opens a margin for margin labels.
1229             "labels" : {
1230             // Margin width. Default is 0 (no margin labels).
1231             // "auto" will automatically reserve a margin if labels are used.
1232             "width" : "auto",
1233             // Alignment for the labels. Default is left.
1234             "align" : "left",
1235             // Alternatively, render labels as comments.
1236             "comment" : "" // "comment", "comment_italic" or "comment_box",
1237             },
1238              
1239             // Alternative songlines with chords in a side column.
1240             // Value is the column position.
1241             // "chordscolumn" : 400,
1242             "chordscolumn" : 0,
1243             "capoheading" : "%{capo|Capo: %{}}",
1244              
1245             // A {titles: left} may conflict with customized formats.
1246             // Set to non-zero to ignore the directive.
1247             "titles-directive-ignore" : false,
1248              
1249             // Chord diagrams.
1250             // A chord diagram consists of a number of cells.
1251             // Cell dimensions are specified by "width" and "height".
1252             // The horizontal number of cells depends on the number of strings.
1253             // The vertical number of cells is "vcells", which should
1254             // be 4 or larger to accomodate most chords.
1255             // The horizontal distance between diagrams is "hspace" cells.
1256             // The vertical distance is "vspace" cells.
1257             // "linewidth" is the thickness of the lines as a fraction of "width".
1258             // Diagrams for all chords of the song can be shown at the
1259             // "top", "bottom" or "right" side of the first page,
1260             // or "below" the last song line.
1261             "diagrams" : {
1262             "show" : "bottom",
1263             "width" : 6, // of a cell
1264             "height" : 6, // of a cell
1265             "vcells" : 4, // vertically
1266             "linewidth" : 0.1, // of a cell width
1267             "hspace" : 3.95, // fraction of width
1268             "vspace" : 3, // fraction of height
1269             "fingers" : true, // show fingering if available
1270             },
1271              
1272             // Keyboard diagrams.
1273             // A keyboard diagram consists of a number of keys.
1274             // Dimensions are specified by "width" (a key) and "height".
1275             // The horizontal distance between diagrams is "hspace" * keys * width.
1276             // The vertical distance is "vspace" * height.
1277             // "linewidth" is the thickness of the lines as a fraction of "width".
1278             // Diagrams for all chords of the song can be shown at the
1279             // "top", "bottom" or "right" side of the first page,
1280             // or "below" the last song line.
1281             "kbdiagrams" : {
1282             "show" : "bottom",
1283             "width" : 4, // of a single key
1284             "height" : 20, // of the diagram
1285             "keys" : 14, // or 7, 10, 14, 17, 21
1286             "base" : "C", // or "F"
1287             "linewidth" : 0.1, // fraction of a single key width
1288             "pressed" : "foreground-medium", // colour of a pressed key
1289             "hspace" : 3.95, // ??
1290             "vspace" : 0.3, // fraction of height
1291             },
1292              
1293             // Grid section lines.
1294             // Suppress when "show" is false, e.g. for singers.
1295             // The width and colour of the cell bar lines can be specified.
1296             // Enable by setting the width to the desired width.
1297             "grids" : {
1298             "cellbar" : {
1299             "width" : 0,
1300             "color" : "foreground-medium",
1301             },
1302             "show": true,
1303             "symbols" : {
1304             "color" : "blue",
1305             },
1306             "volta" : {
1307             "span" : 0.7,
1308             "color" : "blue",
1309             },
1310             },
1311              
1312             // Even/odd pages. A value of -1 denotes odd/even pages.
1313             "even-odd-pages" : 1,
1314             // Align songs to even/odd pages. When greater than 1, force alignment.
1315             "pagealign-songs" : 1,
1316             // PDF file to add as front matter.
1317             "front-matter" : "",
1318             // PDF file to add as back matter.
1319             "back-matter" : "",
1320              
1321             // Formats.
1322             // Pages have two title elements and one footer element. They also
1323             // can have a page of an existing PDF file as underlay (background).
1324             // Topmost is "title". It uses the "title" font as defined further below.
1325             // Second is "subtitle". It uses the "subtitle" font.
1326             // The "footer" uses the "footer" font.
1327             // All elements can have three fields, that are placed to the left side,
1328             // centered, and right side of the page.
1329             // The contents of all fields is defined below. You can use metadata
1330             // items in the fields as shown. By default, the "title" element shows the
1331             // value of metadata item "title", centered on the page. Likewise
1332             // "subtitle".
1333             // NOTE: The "title" and "subtitle" page elements have the same names
1334             // as the default metadata values which may be confusing. To show
1335             // metadata item, e.g. "artist", add its value to one of the
1336             // title/subtitle fields. Don't try to add an artist page element.
1337              
1338             "formats" : {
1339             // Titles/Footers.
1340              
1341             // Titles/footers have 3 parts, which are printed left,
1342             // centered and right.
1343             // For odd/even printing, the 1st background page is used
1344             // for left pages and the next page (if it exists) for right pages.
1345             // For even/odd printing, the order is reversed.
1346              
1347             // By default, a page has:
1348             "default" : {
1349             // No title/subtitle.
1350             "title" : [ "", "", "" ],
1351             "subtitle" : [ "", "", "" ],
1352             // Footer is title -- page number.
1353             "footer" : [ "%{title}", "", "%{page}" ],
1354             // Background page.
1355             "background" : "",
1356             },
1357             // The first page of a song has:
1358             "title" : {
1359             // Title and subtitle.
1360             "title" : [ "", "%{title}", "" ],
1361             "subtitle" : [ "", "%{subtitle}", "" ],
1362             // Footer with page number.
1363             "footer" : [ "", "", "%{page}" ],
1364             // Background page.
1365             "background" : "",
1366             },
1367             // The very first output page is slightly different:
1368             "first" : {
1369             // It has title and subtitle, like normal 'first' pages.
1370             // But no footer.
1371             "footer" : [ "", "", "" ],
1372             // Background page.
1373             "background" : "",
1374             },
1375             },
1376              
1377             // Split marker for syllables that are smaller than chord width.
1378             // split-marker is a 3-part array: 'start', 'repeat', and 'final'.
1379             // 'final' is always printed, last.
1380             // 'start' is printed if there is enough room.
1381             // 'repeat' is printed repeatedly to fill the rest.
1382             // If split-marker is a single string, this is 'start'.
1383             // All elements may be left empty strings.
1384             "split-marker" : [ "", "", "" ],
1385              
1386             // Font families and properties.
1387             // "fontconfig" maps members of font families to physical fonts.
1388             // Optionally, additional properties of the fonts can be specified.
1389             // Physical fonts can be the names of TrueType/OpenType fonts,
1390             // or names of built-in fonts (corefonts).
1391             // Relative filenames are looked up in the fontdir.
1392             // "fontdir" : [ "/usr/share/fonts/liberation", "/home/me/fonts" ],
1393              
1394             "fontdir" : [],
1395             "fontconfig" : {
1396             // alternatives: regular r normal
1397             // alternatives: bold b strong
1398             // alternatives: italic i oblique o emphasis
1399             // alternatives: bolditalic bi italicbold ib boldoblique bo obliquebold ob
1400             "times" : {
1401             "" : "Times-Roman",
1402             "bold" : "Times-Bold",
1403             "italic" : "Times-Italic",
1404             "bolditalic" : "Times-BoldItalic",
1405             },
1406             "helvetica" : {
1407             "" : "Helvetica",
1408             "bold" : "Helvetica-Bold",
1409             "oblique" : "Helvetica-Oblique",
1410             "boldoblique" : "Helvetica-BoldOblique",
1411             },
1412             "courier" : {
1413             "" : "Courier",
1414             "bold" : "Courier-Bold",
1415             "italic" : "Courier-Italic",
1416             "bolditalic" : "Courier-BoldItalic",
1417             },
1418             "dingbats" : {
1419             "" : "ZapfDingbats",
1420             },
1421             },
1422              
1423             // "fonts" maps output elements to fonts as defined in "fontconfig".
1424             // The elements can have a background colour associated.
1425             // Colours are "#RRGGBB" or predefined names like "black", "white",
1426             // and lots of others.
1427             // NOTE: In the built-in config we use only "name" since that can
1428             // be overruled with user settings.
1429              
1430             "fonts" : {
1431             "title" : {
1432             "name" : "Times-Bold",
1433             "size" : 14
1434             },
1435             "text" : {
1436             "name" : "Times-Roman",
1437             "size" : 12
1438             },
1439             "chord" : {
1440             "name" : "Helvetica-Oblique",
1441             "size" : 10
1442             },
1443             "chordfingers" : {
1444             "file" : "ChordProSymbols.ttf", // do not change
1445             "numbercolor" : "background",
1446             },
1447             "comment" : {
1448             "name" : "Helvetica",
1449             "size" : 12,
1450             "background" : "foreground-light"
1451             },
1452             "comment_italic" : {
1453             "name" : "Helvetica-Oblique",
1454             "size" : 12,
1455             },
1456             "comment_box" : {
1457             "name" : "Helvetica",
1458             "size" : 12,
1459             "frame" : 1
1460             },
1461             "tab" : {
1462             "name" : "Courier",
1463             "size" : 10
1464             },
1465             "toc" : {
1466             "name" : "Times-Roman",
1467             "size" : 11
1468             },
1469             "grid" : {
1470             "name" : "Helvetica",
1471             "size" : 10
1472             },
1473             },
1474              
1475             // Element mappings that can be specified, but need not since
1476             // they default to other elements.
1477             // subtitle --> text
1478             // comment --> text
1479             // comment_italic --> chord
1480             // comment_box --> chord
1481             // annotation --> chord
1482             // toc --> text
1483             // grid --> chord
1484             // grid_margin --> comment
1485             // footer --> subtitle @ 60%
1486             // empty --> text
1487             // diagram --> comment
1488             // diagram_base --> text (but at a small size)
1489              
1490             // Bookmarks (PDF outlines).
1491             // fields: primary and (optional) secondary fields.
1492             // label: outline label (omitted if there's only one outline)
1493             // line: text of the outline element
1494             // collapse: initial display is collapsed
1495             // letter: sublevel with first letters if more
1496             // fold: group by primary (NYI)
1497             // omit: ignore this
1498             "outlines" : [
1499             { "fields" : [ "sorttitle", "artist" ],
1500             "label" : "By Title",
1501             "line" : "%{title}%{artist| - %{}}",
1502             "collapse" : false,
1503             "letter" : 5,
1504             "fold" : false,
1505             },
1506             { "fields" : [ "artist", "sorttitle" ],
1507             "label" : "By Artist",
1508             "line" : "%{artist|%{} - }%{title}",
1509             "collapse" : false,
1510             "letter" : 5,
1511             "fold" : false,
1512             },
1513             ],
1514              
1515             // This will show the page layout if non-zero.
1516             "showlayout" : false,
1517              
1518             // CSV generation for MobileSheetsPro. Adapt for other tools.
1519             // Note that the resultant file will conform to RFC 4180.
1520             "csv" : {
1521             "fields" : [
1522             { "name" : "title", "meta" : "title" },
1523             { "name" : "pages", "meta" : "pagerange" },
1524             { "name" : "sort title", "meta" : "sorttitle" },
1525             { "name" : "artists", "meta" : "artist" },
1526             { "name" : "composers", "meta" : "composer" },
1527             { "name" : "collections", "meta" : "collection" },
1528             { "name" : "keys", "meta" : "key_actual" },
1529             { "name" : "years", "meta" : "year" },
1530             // Add "omit" : true to omit a field.
1531             // To add fields with fixed values, use "value":
1532             { "name" : "my_field", "value" : "text", "omit" : true },
1533             ],
1534             // Field separator.
1535             "separator" : ";",
1536             // Values separator.
1537             "vseparator" : "|",
1538             // Restrict CSV to song pages only (do not include matter pages).
1539             "songsonly" : true,
1540             },
1541             },
1542              
1543             // Settings for ChordPro backend.
1544             "chordpro" : {
1545             // Style of chorus.
1546             "chorus" : {
1547             // Recall style: Print the tag using the type.
1548             // Alternatively quote the lines of the preceding chorus.
1549             // If no tag+type or quote: use {chorus}.
1550             // Note: Variant 'msp' always uses {chorus}.
1551             "recall" : {
1552             // "tag" : "Chorus", "type" : "comment",
1553             "tag" : "", "type" : "",
1554             // "quote" : false,
1555             "quote" : false,
1556             },
1557             },
1558             },
1559              
1560             // Settings for HTML backend.
1561             "html" : {
1562             // Stylesheet links.
1563             "styles" : {
1564             "display" : "chordpro.css",
1565             "print" : "chordpro_print.css",
1566             },
1567             },
1568              
1569             // Settings for LaTeX backend.
1570             "latex" : {
1571             "template_include_path" : [ ],
1572             "templates" : {
1573             "songbook" : "songbook.tt",
1574             "comment" : "comment.tt",
1575             "image" : "image.tt"
1576             }
1577             },
1578              
1579             // Settings for Text backend.
1580             "text" : {
1581             // Style of chorus.
1582             "chorus" : {
1583             // Recall style: Print the tag using the type.
1584             // Alternatively quote the lines of the preceding chorus.
1585             // If no tag+type or quote: use {chorus}.
1586             // Note: Variant 'msp' always uses {chorus}.
1587             "recall" : {
1588             // "tag" : "Chorus", "type" : "comment",
1589             "tag" : "", "type" : "",
1590             // "quote" : false,
1591             "quote" : false,
1592             },
1593             },
1594             },
1595              
1596             // Settings for A2Crd.
1597             "a2crd" : {
1598             // Treat leading lyrics lines as title/subtitle lines.
1599             "infer-titles" : true,
1600             // Classification algorithm.
1601             "classifier" : "pct_chords",
1602             // Tab stop width for tab expansion. Set to zero to disable.
1603             "tabstop" : 8,
1604             },
1605              
1606             // Settings for the parser/preprocessor.
1607             // For selected lines, you can specify a series of
1608             // { "target" : "xxx", "replace" : "yyy" }
1609             // Every occurrence of "xxx" will be replaced by "yyy".
1610             // Use wisely.
1611             "parser" : {
1612             "preprocess" : {
1613             // All lines.
1614             "all" : [],
1615             // Directives.
1616             "directive" : [],
1617             // Song lines (lyrics) only.
1618             "songline" : [],
1619             },
1620             },
1621              
1622             // For (debugging (internal use only)).
1623             "debug" : {
1624             "chords" : 0,
1625             "config" : 0,
1626             "echo" : 0,
1627             "fonts" : 0,
1628             "images" : 0,
1629             "layout" : 0,
1630             "meta" : 0,
1631             "mma" : 0,
1632             "spacing" : 0,
1633             "song" : 0,
1634             "songfull" : 0,
1635             "csv" : 0,
1636             "abc" : 0,
1637             "ly" : 0,
1638             // For temporary use.
1639             "x1" : 0,
1640             "x2" : 0,
1641             "x3" : 0,
1642             },
1643              
1644             }
1645             // End of config.
1646             End_Of_Config
1647             }
1648              
1649             # For convenience.
1650              
1651 214     214 0 518 sub diagram_strings ( $self ) {
  214         354  
  214         318  
1652             # tuning is usually removed from the config.
1653             # scalar( @{ $self->{tuning} } );
1654 214         711 ChordPro::Chords::strings();
1655             }
1656              
1657 0     0 0   sub diagram_keys ( $self ) {
  0            
  0            
1658 0           $self->{kbdiagrams}->{keys};
1659             }
1660              
1661             # For debugging messages.
1662 0     0 0   sub qd ( $val, $compact = 0 ) {
  0            
  0            
  0            
1663 79     79   2226 use Data::Dumper qw();
  79         13199  
  79         25752  
1664 0           local $Data::Dumper::Sortkeys = 1;
1665 0           local $Data::Dumper::Indent = 1;
1666 0           local $Data::Dumper::Quotekeys = 0;
1667 0           local $Data::Dumper::Deparse = 1;
1668 0           local $Data::Dumper::Terse = 1;
1669 0           local $Data::Dumper::Trailingcomma = !$compact;
1670 0           local $Data::Dumper::Useperl = 1;
1671 0           local $Data::Dumper::Useqq = 0; # I want unicode visible
1672 0           my $x = Data::Dumper::Dumper($val);
1673 0 0         if ( $compact ) {
1674 0           $x =~ s/^bless\( (.*), '[\w:]+' \)$/$1/s;
1675 0           $x =~ s/\s+/ /gs;
1676             }
1677 0 0         defined wantarray ? $x : warn($x,"\n");
1678             }
1679              
1680             unless ( caller ) {
1681             binmode STDOUT => ':utf8';
1682             print( default_config() );
1683             exit;
1684             }
1685              
1686             1;
1687              
1688             =head1 DEFAULT CONFIGURATION
1689              
1690             The default configuration as built in. User and system
1691             configs go on top of this one.
1692              
1693             See L for
1694             extensive details and examples.
1695              
1696