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   20 use v5.24;
  2         6  
3 2     2   9 use warnings;
  2         4  
  2         92  
4             { our $VERSION = '0.002' }
5              
6 2     2   29 use Carp;
  2         4  
  2         95  
7 2     2   21 use English;
  2         4  
  2         12  
8 2     2   2126 use Template::Perlish ();
  2         6588  
  2         67  
9 2     2   13 use Data::Resolver ();
  2         3  
  2         28  
10 2     2   1970 use PDF::Builder;
  2         461266  
  2         69  
11              
12 2     2   890 use Moo;
  2         12706  
  2         10  
13 2     2   2733 use experimental qw< signatures >;
  2         16  
  2         26  
14 2     2   258 no warnings qw< experimental::signatures >;
  2         4  
  2         49  
15              
16 2     2   862 use namespace::clean;
  2         24423  
  2         10  
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   166 sub _build__defaults ($self) { return {} }
  17         27  
  17         30  
  17         48  
35 16     16   181 sub _build__fonts ($self) { return {} }
  16         35  
  16         28  
  16         95  
36 16     16   177 sub _build__pdf ($self) { return PDF::Builder->new }
  16         26  
  16         24  
  16         104  
37              
38 17     17 1 3867 sub render ($self, $data) {
  17         29  
  17         28  
  17         26  
39 17         358 $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   727 sub _real_render ($self) {
  17         25  
  17         25  
47 17         76 for my $command ($self->commands->@*) {
48 66         248444 my $op = $command->{op} =~ s{-}{_}rgmxs;
49 66 50       496 my $method = $self->can('_op_' . $op)
50             or croak "unsupported op<$command->{op}>";
51 66         218 $self->$method($command);
52             } ## end for my $command ($self->...)
53 16         489 return $self->_pdf;
54             } ## end sub _real_render
55              
56 193     193   277 sub _tpr ($self, $tmpl) {
  193         224  
  193         257  
  193         205  
57 193         3403 return Template::Perlish::render($tmpl, $self->_data,
58             {functions => $self->functions});
59             }
60              
61 49     49   95 sub _expand ($self, $command, @keys) {
  49         70  
  49         67  
  49         163  
  49         65  
62 49         129 my %auto_expand = map { $_ => 1 } @keys;
  276         577  
63 49         1367 my %overall = ($self->_defaults->%*, $command->%*);
64 49         746 my %retval;
65 49         300 for my $key (sort { $a cmp $b } keys %overall) {
  536         744  
66 307         234174 my $nkey = $key =~ s{-}{_}rgmxs;
67 307 50       721 next if exists $retval{$nkey};
68 307         468 my $value = $overall{$key};
69 307 100       928 $retval{$nkey} = $auto_expand{$nkey} ? $self->_tpr($value) : $value;
70             } ## end for my $key (sort { $a ...})
71 48         32294 return \%retval;
72             } ## end sub _expand
73              
74 64 100   64   542 sub __pageno ($input) { return $input eq 'last' ? 0 : $input }
  64         127  
  64         99  
  64         366  
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   35 sub _font ($s, $key) {
  16         28  
  16         34  
  16         24  
100 16 50       319 if (! defined($s->_fonts->{$key})) {
101 16 50       98 $key = $key =~ m{\A fc: (.*) \z}mxs ? __fc_list($1)
    50          
102             : $key =~ m{\A file: (.*) \z}mxs ? $1
103             : $key;
104 16         264 $s->_fonts->{$key} = $s->_pdf->font($key);
105             }
106 16         630077 return $s->_fonts->{$key};
107             }
108              
109 16     16   42 sub _op_add_image ($self, $command) {
  16         33  
  16         30  
  16         32  
110 16         93 my $opts = $self->_expand($command, qw< page path x y width height >);
111 16   50     315 my $page = $self->_pdf->open_page(__pageno($opts->{page} // 'last'));
112 16         911 my $image = $self->_pdf->image($opts->{path});
113 16         450355 $page->object($image, $opts->@{qw< x y width height >});
114 16         11407 return;
115             } ## end sub _op_add_image
116              
117 17     17   23 sub _op_add_page ($self, $command) {
  17         29  
  17         25  
  17         30  
118 17         81 my $opts =
119             $self->_expand($command, qw< page from from_path from_page >);
120 16   50     99 my $target_n = __pageno($opts->{page} // 'last');
121             defined(my $source_path = $opts->{from} // $opts->{from_path})
122 16 50 33     76 or return $self->_pdf->page($target_n);
123 16         145 my $source = PDF::Builder->open($source_path);
124 16   50     121917 my $source_n = __pageno($opts->{from_page} // 'last');
125 16         458 return $self->_pdf->import_page($source, $source_n, $target_n);
126             } ## end sub _op_add_page
127              
128 16     16   41 sub _op_add_text ($self, $command) {
  16         42  
  16         32  
  16         31  
129 16         81 my $opts =
130             $self->_expand($command, qw< align page font font_family font_size x y >);
131              
132             my $content =
133 16         125 $self->_render_text($opts->@{qw< text text_template text_var >});
134              
135 16   33     19507 my $font = $self->_font($opts->{font} // $opts->{font_family});
136 16         170 my $font_size = $opts->{font_size};
137              
138 16   50     75 my ($x, $y) = map { $_ // 0 } $opts->@{qw< x y >};
  32         123  
139              
140 16   50     102 my $align = $opts->{align} // 'start';
141 16 50       62 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     266 my $page = $self->_pdf->open_page(__pageno($opts->{page} // 'last'));
147 16         740 my $text = $page->text;
148 16         5736 $text->position($x, $y);
149 16         1750 $text->font($font, $opts->{font_size});
150 16   50     3442 $text->text($content // '');
151              
152 16         216494 return $self;
153             } ## end sub _op_add_text
154              
155 16     16   30 sub _render_text ($self, $plain, $template, $crumbs) {
  16         28  
  16         37  
  16         24  
  16         23  
  16         30  
156 16 50       66 return $plain if defined $plain;
157 16 50       77 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   34 sub _op_set_defaults ($self, $command) {
  17         32  
  17         29  
  17         23  
164 17         291 my $defaults = $self->_defaults;
165 17         108 while (my ($key, $value) = each $command->%*) {
166 51 100       117 next if $key eq 'op';
167 34 50       70 if (defined $value) { $defaults->{$key} = $value }
  34         111  
168 0         0 else { delete $defaults->{$key} }
169             }
170 17         31 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;