line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package main; |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
our $options; |
6
|
|
|
|
|
|
|
our $config; |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
package ChordPro::Output::Text; |
9
|
|
|
|
|
|
|
|
10
|
5
|
|
|
5
|
|
1037
|
use ChordPro::Output::Common; |
|
5
|
|
|
|
|
12
|
|
|
5
|
|
|
|
|
351
|
|
11
|
|
|
|
|
|
|
|
12
|
5
|
|
|
5
|
|
35
|
use strict; |
|
5
|
|
|
|
|
13
|
|
|
5
|
|
|
|
|
167
|
|
13
|
5
|
|
|
5
|
|
29
|
use warnings; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
11066
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
sub generate_songbook { |
16
|
21
|
|
|
21
|
0
|
77
|
my ( $self, $sb ) = @_; |
17
|
21
|
|
|
|
|
57
|
my @book; |
18
|
|
|
|
|
|
|
|
19
|
21
|
|
|
|
|
46
|
foreach my $song ( @{$sb->{songs}} ) { |
|
21
|
|
|
|
|
82
|
|
20
|
21
|
50
|
|
|
|
77
|
if ( @book ) { |
21
|
0
|
0
|
|
|
|
0
|
push(@book, "") if $options->{'backend-option'}->{tidy}; |
22
|
0
|
|
|
|
|
0
|
push(@book, "-- New song"); |
23
|
|
|
|
|
|
|
} |
24
|
21
|
|
|
|
|
48
|
push(@book, @{generate_song($song)}); |
|
21
|
|
|
|
|
96
|
|
25
|
|
|
|
|
|
|
} |
26
|
|
|
|
|
|
|
|
27
|
21
|
|
|
|
|
58
|
push( @book, ""); |
28
|
21
|
|
|
|
|
77
|
\@book; |
29
|
|
|
|
|
|
|
} |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
my $single_space = 0; # suppress chords line when empty |
32
|
|
|
|
|
|
|
my $lyrics_only = 0; # suppress all chords lines |
33
|
|
|
|
|
|
|
my $chords_under = 0; # chords under lyrics |
34
|
|
|
|
|
|
|
my $layout = Text::Layout::Text->new; |
35
|
|
|
|
|
|
|
my $rechorus; |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub upd_config { |
38
|
24
|
|
|
24
|
0
|
66
|
$lyrics_only = $config->{settings}->{'lyrics-only'}; |
39
|
24
|
|
|
|
|
70
|
$chords_under = $config->{settings}->{'chords-under'}; |
40
|
24
|
|
|
|
|
108
|
$rechorus = $config->{text}->{chorus}->{recall}; |
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
sub generate_song { |
44
|
21
|
|
|
21
|
0
|
60
|
my ( $s ) = @_; |
45
|
|
|
|
|
|
|
|
46
|
21
|
|
|
|
|
87
|
my $tidy = $options->{'backend-option'}->{tidy}; |
47
|
21
|
|
|
|
|
57
|
$single_space = $options->{'single-space'}; |
48
|
21
|
|
|
|
|
99
|
upd_config(); |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
$s->structurize |
51
|
21
|
50
|
50
|
|
|
156
|
if ( $options->{'backend-option'}->{structure} // '' ) eq 'structured'; |
52
|
|
|
|
|
|
|
|
53
|
21
|
|
|
|
|
46
|
my @s; |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
push(@s, "-- Title: " . $s->{title}) |
56
|
21
|
50
|
|
|
|
131
|
if defined $s->{title}; |
57
|
21
|
100
|
|
|
|
90
|
if ( defined $s->{subtitle} ) { |
58
|
6
|
|
|
|
|
13
|
push(@s, map { +"-- Subtitle: $_" } @{$s->{subtitle}}); |
|
6
|
|
|
|
|
24
|
|
|
6
|
|
|
|
|
22
|
|
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
|
61
|
21
|
50
|
|
|
|
66
|
push(@s, "") if $tidy; |
62
|
|
|
|
|
|
|
|
63
|
21
|
|
|
|
|
53
|
my $ctx = ""; |
64
|
21
|
|
|
|
|
53
|
my @elts = @{$s->{body}}; |
|
21
|
|
|
|
|
92
|
|
65
|
21
|
|
|
|
|
70
|
while ( @elts ) { |
66
|
309
|
|
|
|
|
514
|
my $elt = shift(@elts); |
67
|
|
|
|
|
|
|
|
68
|
309
|
100
|
|
|
|
744
|
if ( $elt->{context} ne $ctx ) { |
69
|
54
|
100
|
|
|
|
169
|
push(@s, "-- End of $ctx") if $ctx; |
70
|
54
|
100
|
|
|
|
186
|
push(@s, "-- Start of $ctx") if $ctx = $elt->{context}; |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
|
73
|
309
|
100
|
|
|
|
1072
|
if ( $elt->{type} eq "empty" ) { |
74
|
|
|
|
|
|
|
push(@s, "***SHOULD NOT HAPPEN***") |
75
|
81
|
50
|
|
|
|
188
|
if $s->{structure} eq 'structured'; |
76
|
81
|
|
|
|
|
171
|
push(@s, ""); |
77
|
81
|
|
|
|
|
159
|
next; |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
228
|
100
|
|
|
|
472
|
if ( $elt->{type} eq "colb" ) { |
81
|
3
|
|
|
|
|
10
|
push(@s, "-- Column break"); |
82
|
3
|
|
|
|
|
7
|
next; |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
225
|
100
|
|
|
|
449
|
if ( $elt->{type} eq "newpage" ) { |
86
|
3
|
|
|
|
|
9
|
push(@s, "-- New page"); |
87
|
3
|
|
|
|
|
9
|
next; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
222
|
100
|
|
|
|
463
|
if ( $elt->{type} eq "songline" ) { |
91
|
105
|
|
|
|
|
267
|
push(@s, songline( $s, $elt )); |
92
|
105
|
|
|
|
|
827
|
next; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
|
95
|
117
|
100
|
|
|
|
263
|
if ( $elt->{type} eq "tabline" ) { |
96
|
24
|
|
|
|
|
59
|
push(@s, $elt->{text}); |
97
|
24
|
|
|
|
|
51
|
next; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
93
|
50
|
|
|
|
229
|
if ( $elt->{type} eq "chorus" ) { |
101
|
0
|
0
|
|
|
|
0
|
push(@s, "") if $tidy; |
102
|
0
|
|
|
|
|
0
|
push(@s, "-- Start of chorus*"); |
103
|
0
|
|
|
|
|
0
|
foreach my $e ( @{$elt->{body}} ) { |
|
0
|
|
|
|
|
0
|
|
104
|
0
|
0
|
|
|
|
0
|
if ( $e->{type} eq "empty" ) { |
105
|
0
|
|
|
|
|
0
|
push(@s, ""); |
106
|
0
|
|
|
|
|
0
|
next; |
107
|
|
|
|
|
|
|
} |
108
|
0
|
0
|
|
|
|
0
|
if ( $e->{type} eq "songline" ) { |
109
|
0
|
|
|
|
|
0
|
push(@s, songline( $s, $e )); |
110
|
0
|
|
|
|
|
0
|
next; |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
} |
113
|
0
|
|
|
|
|
0
|
push(@s, "-- End of chorus*"); |
114
|
0
|
0
|
|
|
|
0
|
push(@s, "") if $tidy; |
115
|
0
|
|
|
|
|
0
|
next; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
93
|
100
|
|
|
|
232
|
if ( $elt->{type} eq "rechorus" ) { |
119
|
15
|
50
|
0
|
|
|
56
|
if ( $rechorus->{quote} ) { |
|
|
0
|
|
|
|
|
|
120
|
15
|
|
|
|
|
23
|
unshift( @elts, @{ $elt->{chorus} } ); |
|
15
|
|
|
|
|
39
|
|
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
elsif ( $rechorus->{type} && $rechorus->{tag} ) { |
123
|
0
|
|
|
|
|
0
|
push( @s, "{".$rechorus->{type}.": ".$rechorus->{tag}."}" ); |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
else { |
126
|
0
|
|
|
|
|
0
|
push( @s, "{chorus}" ); |
127
|
|
|
|
|
|
|
} |
128
|
15
|
|
|
|
|
31
|
next; |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
|
131
|
78
|
50
|
|
|
|
192
|
if ( $elt->{type} eq "tab" ) { |
132
|
0
|
0
|
|
|
|
0
|
push(@s, "") if $tidy; |
133
|
0
|
|
|
|
|
0
|
push(@s, "-- Start of tab"); |
134
|
0
|
|
|
|
|
0
|
push(@s, map { $_->{text} } @{$elt->{body}} ); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
135
|
0
|
|
|
|
|
0
|
push(@s, "-- End of tab"); |
136
|
0
|
0
|
|
|
|
0
|
push(@s, "") if $tidy; |
137
|
0
|
|
|
|
|
0
|
next; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
78
|
50
|
|
|
|
187
|
if ( $elt->{type} eq "verse" ) { |
141
|
0
|
0
|
|
|
|
0
|
push(@s, "") if $tidy; |
142
|
0
|
|
|
|
|
0
|
push(@s, "-- Start of verse"); |
143
|
0
|
|
|
|
|
0
|
foreach my $e ( @{$elt->{body}} ) { |
|
0
|
|
|
|
|
0
|
|
144
|
0
|
0
|
|
|
|
0
|
if ( $e->{type} eq "empty" ) { |
145
|
|
|
|
|
|
|
push(@s, "***SHOULD NOT HAPPEN***") |
146
|
0
|
0
|
|
|
|
0
|
if $s->{structure} eq 'structured'; |
147
|
0
|
|
|
|
|
0
|
next; |
148
|
|
|
|
|
|
|
} |
149
|
0
|
0
|
|
|
|
0
|
if ( $e->{type} eq "songline" ) { |
150
|
0
|
|
|
|
|
0
|
push(@s, songline( $s, $e )); |
151
|
0
|
|
|
|
|
0
|
next; |
152
|
|
|
|
|
|
|
} |
153
|
0
|
0
|
|
|
|
0
|
if ( $e->{type} eq "comment" ) { |
154
|
0
|
|
|
|
|
0
|
push(@s, "-c- " . $e->{text}); |
155
|
0
|
|
|
|
|
0
|
next; |
156
|
|
|
|
|
|
|
} |
157
|
0
|
0
|
|
|
|
0
|
if ( $e->{type} eq "comment_italic" ) { |
158
|
0
|
|
|
|
|
0
|
push(@s, "-i- " . $e->{text}); |
159
|
0
|
|
|
|
|
0
|
next; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
} |
162
|
0
|
|
|
|
|
0
|
push(@s, "-- End of verse"); |
163
|
0
|
0
|
|
|
|
0
|
push(@s, "") if $tidy; |
164
|
0
|
|
|
|
|
0
|
next; |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
|
167
|
78
|
100
|
|
|
|
315
|
if ( $elt->{type} =~ /^comment(?:_italic|_box)?$/ ) { |
168
|
42
|
50
|
|
|
|
120
|
push(@s, "") if $tidy; |
169
|
42
|
|
|
|
|
97
|
my $text = $elt->{text}; |
170
|
42
|
100
|
|
|
|
118
|
if ( $elt->{chords} ) { |
171
|
12
|
|
|
|
|
30
|
$text = ""; |
172
|
12
|
|
|
|
|
23
|
for ( 0..$#{ $elt->{chords} } ) { |
|
12
|
|
|
|
|
59
|
|
173
|
|
|
|
|
|
|
$text .= "[" . $elt->{chords}->[$_]->key . "]" |
174
|
24
|
100
|
|
|
|
137
|
if $elt->{chords}->[$_] ne ""; |
175
|
24
|
|
|
|
|
62
|
$text .= $elt->{phrases}->[$_]; |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
# $text = fmt_subst( $s, $text ); |
179
|
42
|
|
|
|
|
155
|
push(@s, "-- $text"); |
180
|
42
|
50
|
|
|
|
111
|
push(@s, "") if $tidy; |
181
|
42
|
|
|
|
|
113
|
next; |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
36
|
50
|
|
|
|
91
|
if ( $elt->{type} eq "image" ) { |
185
|
0
|
|
|
|
|
0
|
my @args = ( "image:", $elt->{uri} ); |
186
|
0
|
|
|
|
|
0
|
while ( my($k,$v) = each( %{ $elt->{opts} } ) ) { |
|
0
|
|
|
|
|
0
|
|
187
|
0
|
|
|
|
|
0
|
push( @args, "$k=$v" ); |
188
|
|
|
|
|
|
|
} |
189
|
0
|
|
|
|
|
0
|
foreach ( @args ) { |
190
|
0
|
0
|
|
|
|
0
|
next unless /\s/; |
191
|
0
|
|
|
|
|
0
|
$_ = '"' . $_ . '"'; |
192
|
|
|
|
|
|
|
} |
193
|
0
|
|
|
|
|
0
|
push( @s, "+ @args" ); |
194
|
0
|
|
|
|
|
0
|
next; |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
36
|
100
|
|
|
|
89
|
if ( $elt->{type} eq "set" ) { |
198
|
33
|
50
|
|
|
|
146
|
if ( $elt->{name} eq "lyrics-only" ) { |
|
|
100
|
|
|
|
|
|
199
|
|
|
|
|
|
|
$lyrics_only = $elt->{value} |
200
|
0
|
0
|
|
|
|
0
|
unless $lyrics_only > 1; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
# Arbitrary config values. |
203
|
|
|
|
|
|
|
elsif ( $elt->{name} =~ /^(text\..+)/ ) { |
204
|
3
|
|
|
|
|
26
|
my @k = split( /[.]/, $1 ); |
205
|
3
|
|
|
|
|
9
|
my $cc = {}; |
206
|
3
|
|
|
|
|
9
|
my $c = \$cc; |
207
|
3
|
|
|
|
|
10
|
foreach ( @k ) { |
208
|
12
|
|
|
|
|
34
|
$c = \($$c->{$_}); |
209
|
|
|
|
|
|
|
} |
210
|
3
|
|
|
|
|
8
|
$$c = $elt->{value}; |
211
|
3
|
|
|
|
|
21
|
$config->augment($cc); |
212
|
3
|
|
|
|
|
35
|
upd_config(); |
213
|
|
|
|
|
|
|
} |
214
|
33
|
|
|
|
|
92
|
next; |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
|
217
|
3
|
50
|
|
|
|
16
|
if ( $elt->{type} eq "control" ) { |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
} |
220
|
21
|
100
|
|
|
|
115
|
push(@s, "-- End of $ctx") if $ctx; |
221
|
|
|
|
|
|
|
|
222
|
21
|
|
|
|
|
190
|
\@s; |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
sub songline { |
226
|
105
|
|
|
105
|
0
|
219
|
my ( $song, $elt ) = @_; |
227
|
|
|
|
|
|
|
|
228
|
105
|
|
|
|
|
156
|
my $t_line = ""; |
229
|
330
|
|
|
|
|
960
|
my @phrases = map { $layout->set_markup($_); $layout->render } |
|
330
|
|
|
|
|
15609
|
|
230
|
105
|
|
|
|
|
149
|
@{ $elt->{phrases} }; |
|
105
|
|
|
|
|
242
|
|
231
|
|
|
|
|
|
|
|
232
|
105
|
100
|
66
|
|
|
344
|
if ( $lyrics_only |
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
233
|
|
|
|
|
|
|
or |
234
|
|
|
|
|
|
|
$single_space && ! ( $elt->{chords} && join( "", map { $_?$_->key:"" } @{ $elt->{chords} } ) =~ /\S/ ) |
235
|
|
|
|
|
|
|
) { |
236
|
43
|
|
|
|
|
188
|
$t_line = join( "", @phrases ); |
237
|
43
|
|
|
|
|
218
|
$t_line =~ s/\s+$//; |
238
|
43
|
|
|
|
|
154
|
return $t_line; |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
|
241
|
62
|
100
|
|
|
|
474
|
unless ( $elt->{chords} ) { |
242
|
8
|
|
|
|
|
34
|
return ( "", join( " ", @phrases ) ); |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
|
245
|
54
|
50
|
|
|
|
169
|
if ( my $f = $::config->{settings}->{'inline-chords'} ) { |
246
|
0
|
0
|
|
|
|
0
|
$f = '[%s]' unless $f =~ /^[^%]*\%s[^%]*$/; |
247
|
0
|
|
|
|
|
0
|
$f .= '%s'; |
248
|
0
|
|
|
|
|
0
|
foreach ( 0..$#{$elt->{chords}} ) { |
|
0
|
|
|
|
|
0
|
|
249
|
|
|
|
|
|
|
$t_line .= sprintf( $f, |
250
|
0
|
0
|
|
|
|
0
|
$elt->{chords}->[$_] ? chord( $song, $elt->{chords}->[$_] ) : "", |
251
|
|
|
|
|
|
|
$phrases[$_] ); |
252
|
|
|
|
|
|
|
} |
253
|
0
|
|
|
|
|
0
|
return ( $t_line ); |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
54
|
|
|
|
|
401
|
my $c_line = ""; |
257
|
54
|
|
|
|
|
82
|
foreach my $c ( 0..$#{$elt->{chords}} ) { |
|
54
|
|
|
|
|
174
|
|
258
|
|
|
|
|
|
|
$c_line .= chord( $song, $elt->{chords}->[$c] ) . " " |
259
|
204
|
100
|
|
|
|
750
|
if ref $elt->{chords}->[$c]; |
260
|
204
|
|
|
|
|
499
|
$t_line .= $phrases[$c]; |
261
|
204
|
|
|
|
|
485
|
my $d = length($c_line) - length($t_line); |
262
|
204
|
100
|
|
|
|
450
|
$t_line .= "-" x $d if $d > 0; |
263
|
204
|
100
|
|
|
|
770
|
$c_line .= " " x -$d if $d < 0; |
264
|
|
|
|
|
|
|
} |
265
|
54
|
|
|
|
|
602
|
s/\s+$// for ( $t_line, $c_line ); |
266
|
54
|
50
|
|
|
|
197
|
return $chords_under |
267
|
|
|
|
|
|
|
? ( $t_line, $c_line ) |
268
|
|
|
|
|
|
|
: ( $c_line, $t_line ) |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
sub chord { |
272
|
150
|
|
|
150
|
0
|
290
|
my ( $s, $c ) = @_; |
273
|
150
|
50
|
|
|
|
415
|
return "" unless length($c); |
274
|
150
|
|
|
|
|
466
|
$layout->set_markup($c->chord_display); |
275
|
150
|
|
|
|
|
9152
|
my $t = $layout->render; |
276
|
150
|
50
|
|
|
|
511
|
return $c->info->is_annotation ? "*$t" : $t; |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
# Temporary. Eventually we'll have a decent HTML backend for Text::Layout. |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
package Text::Layout::Text; |
282
|
|
|
|
|
|
|
|
283
|
5
|
|
|
5
|
|
65
|
use parent 'Text::Layout'; |
|
5
|
|
|
|
|
12
|
|
|
5
|
|
|
|
|
59
|
|
284
|
5
|
|
|
5
|
|
63582
|
use ChordPro::Utils qw( fq ); |
|
5
|
|
|
|
|
19
|
|
|
5
|
|
|
|
|
283
|
|
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
# Eliminate warning when HTML backend is loaded together with Text backend. |
287
|
5
|
|
|
5
|
|
34
|
no warnings 'redefine'; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
798
|
|
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
sub new { |
290
|
4
|
|
|
4
|
|
15
|
my ( $pkg, @data ) = @_; |
291
|
4
|
|
|
|
|
34
|
my $self = $pkg->SUPER::new; |
292
|
4
|
|
|
|
|
66
|
$self; |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
sub render { |
296
|
480
|
|
|
480
|
|
936
|
my ( $self ) = @_; |
297
|
480
|
|
|
|
|
739
|
my $res = ""; |
298
|
480
|
|
|
|
|
660
|
foreach my $fragment ( @{ $self->{_content} } ) { |
|
480
|
|
|
|
|
1077
|
|
299
|
474
|
50
|
|
|
|
1287
|
next unless length($fragment->{text}); |
300
|
474
|
|
|
|
|
1260
|
$res .= fq($fragment->{text}); |
301
|
|
|
|
|
|
|
} |
302
|
480
|
|
|
|
|
1266
|
$res; |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
1; |