line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package PDF::Collage::Template; |
2
|
2
|
|
|
2
|
|
25
|
use v5.24; |
|
2
|
|
|
|
|
7
|
|
3
|
2
|
|
|
2
|
|
10
|
use warnings; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
90
|
|
4
|
|
|
|
|
|
|
{ our $VERSION = '0.001001' } |
5
|
|
|
|
|
|
|
|
6
|
2
|
|
|
2
|
|
34
|
use Carp; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
114
|
|
7
|
2
|
|
|
2
|
|
25
|
use English; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
11
|
|
8
|
2
|
|
|
2
|
|
2122
|
use Template::Perlish (); |
|
2
|
|
|
|
|
8014
|
|
|
2
|
|
|
|
|
46
|
|
9
|
2
|
|
|
2
|
|
13
|
use Data::Resolver (); |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
30
|
|
10
|
2
|
|
|
2
|
|
1977
|
use PDF::Builder; |
|
2
|
|
|
|
|
573683
|
|
|
2
|
|
|
|
|
82
|
|
11
|
|
|
|
|
|
|
|
12
|
2
|
|
|
2
|
|
1144
|
use Moo; |
|
2
|
|
|
|
|
16532
|
|
|
2
|
|
|
|
|
12
|
|
13
|
2
|
|
|
2
|
|
3587
|
use experimental qw< signatures >; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
19
|
|
14
|
2
|
|
|
2
|
|
434
|
no warnings qw< experimental::signatures >; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
65
|
|
15
|
|
|
|
|
|
|
|
16
|
2
|
|
|
2
|
|
1124
|
use namespace::clean; |
|
2
|
|
|
|
|
30744
|
|
|
2
|
|
|
|
|
15
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
has commands => (is => ro => required => 1); |
19
|
|
|
|
|
|
|
has functions => (is => 'lazy'); |
20
|
|
|
|
|
|
|
has logger => (is => 'lazy'); |
21
|
|
|
|
|
|
|
has metadata => (is => 'lazy'); |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
has _data => (is => 'lazy'); |
24
|
|
|
|
|
|
|
has _defaults => (is => 'lazy'); |
25
|
|
|
|
|
|
|
has _fonts => (is => 'lazy'); |
26
|
|
|
|
|
|
|
has _pdf => (is => 'lazy'); |
27
|
|
|
|
|
|
|
|
28
|
0
|
|
|
0
|
|
0
|
sub _build_functions ($self) { return {} } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
29
|
0
|
|
|
0
|
|
0
|
sub _build_logger ($self) { |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
30
|
0
|
|
|
|
|
0
|
eval { require Log::Any; Log::Any->get_logger } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
31
|
|
|
|
|
|
|
} |
32
|
0
|
|
|
0
|
|
0
|
sub _build_metadata ($self) { return {} } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
33
|
0
|
|
|
0
|
|
0
|
sub _build__data ($self) { return {} } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
34
|
17
|
|
|
17
|
|
284
|
sub _build__defaults ($self) { return {} } |
|
17
|
|
|
|
|
32
|
|
|
17
|
|
|
|
|
26
|
|
|
17
|
|
|
|
|
61
|
|
35
|
16
|
|
|
16
|
|
255
|
sub _build__fonts ($self) { return {} } |
|
16
|
|
|
|
|
32
|
|
|
16
|
|
|
|
|
26
|
|
|
16
|
|
|
|
|
103
|
|
36
|
16
|
|
|
16
|
|
268
|
sub _build__pdf ($self) { return PDF::Builder->new } |
|
16
|
|
|
|
|
27
|
|
|
16
|
|
|
|
|
25
|
|
|
16
|
|
|
|
|
133
|
|
37
|
|
|
|
|
|
|
|
38
|
17
|
|
|
17
|
1
|
4592
|
sub render ($self, $data) { |
|
17
|
|
|
|
|
41
|
|
|
17
|
|
|
|
|
50
|
|
|
17
|
|
|
|
|
43
|
|
39
|
17
|
|
|
|
|
429
|
$self->new( # hand over to a disposable clone |
40
|
|
|
|
|
|
|
commands => $self->commands, |
41
|
|
|
|
|
|
|
functions => $self->functions, |
42
|
|
|
|
|
|
|
_data => $data, |
43
|
|
|
|
|
|
|
)->_real_render; |
44
|
|
|
|
|
|
|
} ## end sub render |
45
|
|
|
|
|
|
|
|
46
|
17
|
|
|
17
|
|
904
|
sub _real_render ($self) { |
|
17
|
|
|
|
|
30
|
|
|
17
|
|
|
|
|
29
|
|
47
|
17
|
|
|
|
|
55
|
for my $command ($self->commands->@*) { |
48
|
66
|
|
|
|
|
305681
|
my $op = $command->{op} =~ s{-}{_}rgmxs; |
49
|
66
|
50
|
|
|
|
438
|
my $method = $self->can('_op_' . $op) |
50
|
|
|
|
|
|
|
or croak "unsupported op<$command->{op}>"; |
51
|
66
|
|
|
|
|
279
|
$self->$method($command); |
52
|
|
|
|
|
|
|
} ## end for my $command ($self->...) |
53
|
16
|
|
|
|
|
440
|
return $self->_pdf; |
54
|
|
|
|
|
|
|
} ## end sub _real_render |
55
|
|
|
|
|
|
|
|
56
|
193
|
|
|
193
|
|
286
|
sub _tpr ($self, $tmpl) { |
|
193
|
|
|
|
|
271
|
|
|
193
|
|
|
|
|
293
|
|
|
193
|
|
|
|
|
287
|
|
57
|
193
|
|
|
|
|
3921
|
return Template::Perlish::render($tmpl, $self->_data, |
58
|
|
|
|
|
|
|
{functions => $self->functions}); |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
|
61
|
49
|
|
|
49
|
|
83
|
sub _expand ($self, $command, @keys) { |
|
49
|
|
|
|
|
59
|
|
|
49
|
|
|
|
|
94
|
|
|
49
|
|
|
|
|
171
|
|
|
49
|
|
|
|
|
62
|
|
62
|
49
|
|
|
|
|
101
|
my %auto_expand = map { $_ => 1 } @keys; |
|
276
|
|
|
|
|
595
|
|
63
|
49
|
|
|
|
|
1205
|
my %overall = ($self->_defaults->%*, $command->%*); |
64
|
49
|
|
|
|
|
689
|
my %retval; |
65
|
49
|
|
|
|
|
321
|
for my $key (sort { $a cmp $b } keys %overall) { |
|
519
|
|
|
|
|
829
|
|
66
|
307
|
|
|
|
|
265452
|
my $nkey = $key =~ s{-}{_}rgmxs; |
67
|
307
|
50
|
|
|
|
761
|
next if exists $retval{$nkey}; |
68
|
307
|
|
|
|
|
529
|
my $value = $overall{$key}; |
69
|
307
|
100
|
|
|
|
977
|
$retval{$nkey} = $auto_expand{$nkey} ? $self->_tpr($value) : $value; |
70
|
|
|
|
|
|
|
} ## end for my $key (sort { $a ...}) |
71
|
48
|
|
|
|
|
39555
|
return \%retval; |
72
|
|
|
|
|
|
|
} ## end sub _expand |
73
|
|
|
|
|
|
|
|
74
|
64
|
100
|
|
64
|
|
697
|
sub __pageno ($input) { return $input eq 'last' ? 0 : $input } |
|
64
|
|
|
|
|
125
|
|
|
64
|
|
|
|
|
92
|
|
|
64
|
|
|
|
|
282
|
|
75
|
|
|
|
|
|
|
|
76
|
0
|
|
|
0
|
|
0
|
sub __fc_list ($key) { |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
77
|
0
|
|
|
|
|
0
|
my @command = ('fc-list', $key, qw< file style >); |
78
|
0
|
0
|
|
|
|
0
|
open my $fh, '-|', @command or croak "fc-list: $OS_ERROR"; |
79
|
|
|
|
|
|
|
my @candidates = map { |
80
|
0
|
|
|
|
|
0
|
s{\s+\z}{}mxs; |
|
0
|
|
|
|
|
0
|
|
81
|
0
|
0
|
|
|
|
0
|
my ($filename, $style) = m{\A (.*?): \s* :style=(.*)}mxs |
82
|
|
|
|
|
|
|
or croak "fc-list: unexpected line '$_'"; |
83
|
0
|
|
|
|
|
0
|
my %style = map { $_ => 1 } split m{,}mxs, $style; |
|
0
|
|
|
|
|
0
|
|
84
|
0
|
|
|
|
|
0
|
{filename => $filename, style => \%style}; |
85
|
|
|
|
|
|
|
} <$fh>; |
86
|
0
|
0
|
|
|
|
0
|
return unless @candidates; |
87
|
0
|
0
|
|
|
|
0
|
return $candidates[0]{filename} if @candidates == 1; |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
# get Regular/Normal if exists |
90
|
0
|
|
|
|
|
0
|
for my $candidate (@candidates) { |
91
|
|
|
|
|
|
|
return $candidate->{filename} |
92
|
0
|
0
|
0
|
|
|
0
|
if $candidate->{style}{Regular} || $candidate->{style}{Normal}; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# bail out, request more data |
96
|
0
|
|
|
|
|
0
|
croak "fc-list: too many outputs for '$key'"; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
16
|
|
|
16
|
|
46
|
sub _font ($s, $key) { |
|
16
|
|
|
|
|
34
|
|
|
16
|
|
|
|
|
31
|
|
|
16
|
|
|
|
|
37
|
|
100
|
16
|
50
|
|
|
|
356
|
if (! defined($s->_fonts->{$key})) { |
101
|
16
|
50
|
|
|
|
94
|
$key = $key =~ m{\A fc: (.*) \z}mxs ? __fc_list($1) |
|
|
50
|
|
|
|
|
|
102
|
|
|
|
|
|
|
: $key =~ m{\A file: (.*) \z}mxs ? $1 |
103
|
|
|
|
|
|
|
: $key; |
104
|
16
|
|
|
|
|
278
|
$s->_fonts->{$key} = $s->_pdf->font($key); |
105
|
|
|
|
|
|
|
} |
106
|
16
|
|
|
|
|
770098
|
return $s->_fonts->{$key}; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
16
|
|
|
16
|
|
54
|
sub _op_add_image ($self, $command) { |
|
16
|
|
|
|
|
37
|
|
|
16
|
|
|
|
|
41
|
|
|
16
|
|
|
|
|
29
|
|
110
|
16
|
|
|
|
|
58
|
my $opts = $self->_expand($command, qw< page path x y width height >); |
111
|
16
|
|
50
|
|
|
370
|
my $page = $self->_pdf->open_page(__pageno($opts->{page} // 'last')); |
112
|
16
|
|
|
|
|
935
|
my $image = $self->_pdf->image($opts->{path}); |
113
|
16
|
|
|
|
|
542947
|
$page->object($image, $opts->@{qw< x y width height >}); |
114
|
16
|
|
|
|
|
10692
|
return; |
115
|
|
|
|
|
|
|
} ## end sub _op_add_image |
116
|
|
|
|
|
|
|
|
117
|
17
|
|
|
17
|
|
68
|
sub _op_add_page ($self, $command) { |
|
17
|
|
|
|
|
31
|
|
|
17
|
|
|
|
|
39
|
|
|
17
|
|
|
|
|
33
|
|
118
|
17
|
|
|
|
|
67
|
my $opts = |
119
|
|
|
|
|
|
|
$self->_expand($command, qw< page from from_path from_page >); |
120
|
16
|
|
50
|
|
|
179
|
my $target_n = __pageno($opts->{page} // 'last'); |
121
|
|
|
|
|
|
|
defined(my $source_path = $opts->{from} // $opts->{from_path}) |
122
|
16
|
50
|
33
|
|
|
127
|
or return $self->_pdf->page($target_n); |
123
|
16
|
|
|
|
|
124
|
my $source = PDF::Builder->open($source_path); |
124
|
16
|
|
50
|
|
|
151550
|
my $source_n = __pageno($opts->{from_page} // 'last'); |
125
|
16
|
|
|
|
|
436
|
return $self->_pdf->import_page($source, $source_n, $target_n); |
126
|
|
|
|
|
|
|
} ## end sub _op_add_page |
127
|
|
|
|
|
|
|
|
128
|
16
|
|
|
16
|
|
67
|
sub _op_add_text ($self, $command) { |
|
16
|
|
|
|
|
35
|
|
|
16
|
|
|
|
|
36
|
|
|
16
|
|
|
|
|
25
|
|
129
|
16
|
|
|
|
|
70
|
my $opts = |
130
|
|
|
|
|
|
|
$self->_expand($command, qw< align page font font_family font_size x y >); |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
my $content = |
133
|
16
|
|
|
|
|
197
|
$self->_render_text($opts->@{qw< text text_template text_var >}); |
134
|
|
|
|
|
|
|
|
135
|
16
|
|
33
|
|
|
24142
|
my $font = $self->_font($opts->{font} // $opts->{font_family}); |
136
|
16
|
|
|
|
|
196
|
my $font_size = $opts->{font_size}; |
137
|
|
|
|
|
|
|
|
138
|
16
|
|
50
|
|
|
455
|
my ($x, $y) = map { $_ // 0 } $opts->@{qw< x y >}; |
|
32
|
|
|
|
|
144
|
|
139
|
|
|
|
|
|
|
|
140
|
16
|
|
50
|
|
|
103
|
my $align = $opts->{align} // 'start'; |
141
|
16
|
50
|
|
|
|
65
|
if ($align ne 'start') { |
142
|
0
|
|
|
|
|
0
|
my $width = $font_size * $font->width($content); |
143
|
0
|
0
|
|
|
|
0
|
$x -= $align eq 'end' ? $width : ($width / 2); |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
16
|
|
50
|
|
|
288
|
my $page = $self->_pdf->open_page(__pageno($opts->{page} // 'last')); |
147
|
16
|
|
|
|
|
831
|
my $text = $page->text; |
148
|
16
|
|
|
|
|
5791
|
$text->position($x, $y); |
149
|
16
|
|
|
|
|
2056
|
$text->font($font, $opts->{font_size}); |
150
|
16
|
|
50
|
|
|
4192
|
$text->text($content // ''); |
151
|
|
|
|
|
|
|
|
152
|
16
|
|
|
|
|
254789
|
return $self; |
153
|
|
|
|
|
|
|
} ## end sub _op_add_text |
154
|
|
|
|
|
|
|
|
155
|
16
|
|
|
16
|
|
47
|
sub _render_text ($self, $plain, $template, $crumbs) { |
|
16
|
|
|
|
|
40
|
|
|
16
|
|
|
|
|
31
|
|
|
16
|
|
|
|
|
43
|
|
|
16
|
|
|
|
|
36
|
|
|
16
|
|
|
|
|
26
|
|
156
|
16
|
50
|
|
|
|
64
|
return $plain if defined $plain; |
157
|
16
|
50
|
|
|
|
76
|
return $self->_tpr($template) if defined $template; |
158
|
0
|
0
|
0
|
|
|
0
|
return Template::Perlish::traverse($self->_data, $crumbs) // '' |
159
|
|
|
|
|
|
|
if defined $crumbs; |
160
|
0
|
|
|
|
|
0
|
return; |
161
|
|
|
|
|
|
|
} ## end sub _render_text |
162
|
|
|
|
|
|
|
|
163
|
17
|
|
|
17
|
|
35
|
sub _op_set_defaults ($self, $command) { |
|
17
|
|
|
|
|
45
|
|
|
17
|
|
|
|
|
29
|
|
|
17
|
|
|
|
|
32
|
|
164
|
17
|
|
|
|
|
324
|
my $defaults = $self->_defaults; |
165
|
17
|
|
|
|
|
119
|
while (my ($key, $value) = each $command->%*) { |
166
|
51
|
100
|
|
|
|
152
|
next if $key eq 'op'; |
167
|
34
|
50
|
|
|
|
81
|
if (defined $value) { $defaults->{$key} = $value } |
|
34
|
|
|
|
|
117
|
|
168
|
0
|
|
|
|
|
0
|
else { delete $defaults->{$key} } |
169
|
|
|
|
|
|
|
} |
170
|
17
|
|
|
|
|
45
|
return; |
171
|
|
|
|
|
|
|
} ## end sub _op_set_defaults |
172
|
|
|
|
|
|
|
|
173
|
0
|
|
|
0
|
|
|
sub _default_log ($self, $command) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
174
|
0
|
|
|
|
|
|
warn "[$command->{level}] $command->{message}\n"; |
175
|
0
|
|
|
|
|
|
return $self; |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
0
|
|
|
0
|
|
|
sub _op_log ($self, $command) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
179
|
0
|
0
|
|
|
|
|
my $logger = $self->logger or return $self->_default_log($command); |
180
|
0
|
0
|
0
|
|
|
|
my $method = $logger->can(lc($command->{level}) // 'info') |
181
|
|
|
|
|
|
|
or return $self->_default_log($command); |
182
|
0
|
|
|
|
|
|
$logger->$method($command->{message}); |
183
|
0
|
|
|
|
|
|
return $self; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
1; |