File Coverage

blib/lib/PDF/Collage/Template.pm
Criterion Covered Total %
statement 145 198 73.2
branch 16 44 36.3
condition 9 27 33.3
subroutine 25 32 78.1
pod 1 1 100.0
total 196 302 64.9


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;