PDF-Collage

 view release on metacpan or  search on metacpan

lib/PDF/Collage/Template.pm  view on Meta::CPAN

has _defaults  => (is => 'lazy');
has _fonts     => (is => 'lazy');
has _pdf       => (is => 'lazy');

sub _build_functions ($self) { return {} }
sub _build_logger    ($self) {
   eval { require Log::Any; Log::Any->get_logger }
}
sub _build_metadata   ($self) { return {} }
sub _build__src_cache ($self) { return {} }
sub _build__data      ($self) { return {} }
sub _build__defaults  ($self) { return {} }
sub _build__fonts     ($self) { return {} }
sub _build__pdf       ($self) { return PDF::Builder->new }

sub render ($self, $data) {
   $self->new(    # hand over to a disposable clone
      commands  => $self->commands,
      functions => $self->functions,
      _data     => $data,
   )->_real_render;
} ## end sub render

sub _real_render ($self) {
   for my $command ($self->commands->@*) {
      my $op     = $command->{op} =~ s{-}{_}rgmxs;
      my $method = $self->can('_op_' . $op)
        or croak "unsupported op<$command->{op}>";
      $self->$method($command);
   } ## end for my $command ($self->...)
   return $self->_pdf;
} ## end sub _real_render

sub _tpr ($self, $tmpl) {
   return Template::Perlish::render($tmpl, $self->_data,
      {functions => $self->functions});
}

sub _expand ($self, $command, @keys) {
   my %auto_expand = map { $_ => 1 } @keys;
   my %overall     = ($self->_defaults->%*, $command->%*);
   my %retval;
   for my $key (sort { $a cmp $b } keys %overall) {
      my $nkey = $key =~ s{-}{_}rgmxs;
      next if exists $retval{$nkey};
      my $value = $overall{$key};
      $retval{$nkey} = $auto_expand{$nkey} ? $self->_tpr($value) : $value;
   } ## end for my $key (sort { $a ...})
   return \%retval;
} ## end sub _expand

sub __pageno ($input)   { return $input eq 'last' ? 0 : $input }

sub __fc_list ($key) {
   my @command = ('fc-list', $key, qw< file style >);
   open my $fh, '-|', @command or croak "fc-list: $OS_ERROR";
   my @candidates = map {
      s{\s+\z}{}mxs;
      my ($filename, $style) = m{\A (.*?): \s* :style=(.*)}mxs
         or croak "fc-list: unexpected line '$_'";
      my %style = map { $_ => 1 } split m{,}mxs, $style;
      {filename => $filename, style => \%style};
   } <$fh>;
   return unless @candidates;
   return $candidates[0]{filename} if @candidates == 1;

   # get Regular/Normal if exists
   for my $candidate (@candidates) {
      return $candidate->{filename}
         if $candidate->{style}{Regular} || $candidate->{style}{Normal};
   }

   # bail out, request more data
   croak "fc-list: too many outputs for '$key'";
}

sub _font    ($s, $key) {
   if (! defined($s->_fonts->{$key})) {
      $key = $key =~ m{\A fc: (.*) \z}mxs ? __fc_list($1)
         : $key =~ m{\A file: (.*) \z}mxs ? $1
         :                                  $key;
      $s->_fonts->{$key} = $s->_pdf->font($key);
   }
   return $s->_fonts->{$key};
}

sub _op_add_image ($self, $command) {
   my $opts  = $self->_expand($command, qw< page path x y width height >);
   my $page  = $self->_pdf->open_page(__pageno($opts->{page} // 'last'));
   my $image = $self->_pdf->image($opts->{path});
   $page->object($image, $opts->@{qw< x y width height >});
   return;
} ## end sub _op_add_image

sub __parse_pages ($input) {
   return $input if ref($input); # already represented as an array
   my @pages = map {
      my ($from, $to) = split m{-}mxs, $_, 2;
      defined($to) ? ($from .. $to) : $from;
   } split m{[\s,]+}mxs, $input;
   return \@pages;
}

sub _op_add_page ($self, $command) {
   my $opts =
     $self->_expand($command, qw< page from from_path from_page >);
   my $target_n = __pageno($opts->{page} // 'last');
   defined(my $source_path = $opts->{from} // $opts->{from_path})
     or return $self->_pdf->page($target_n);
   my $source = $self->_src_cache->{$source_path}
      //= PDF::Builder->open($source_path);

   my $retval;
   my $source_ns = __parse_pages($opts->{from_page} // 'last');
   for my $sn ($source_ns->@*) {
      my $source_n = __pageno($sn);
      $retval = $self->_pdf->import_page($source, $source_n, $target_n);
      $target_n++ if $target_n; # only advance if not 0 = last
   }
   return $retval;
} ## end sub _op_add_page

sub _op_add_text ($self, $command) {
   my $opts =
     $self->_expand($command, qw< align page font font_family font_size x y >);

   my $content =
     $self->_render_text($opts->@{qw< text text_template text_var >});

   my $font = $self->_font($opts->{font} // $opts->{font_family});
   my $font_size = $opts->{font_size};

   my ($x, $y) = map { $_ // 0 } $opts->@{qw< x y >};

   my $align = $opts->{align} // 'start';
   if ($align ne 'start') {
      my $width = $font_size * $font->width($content);
      $x -= $align eq 'end' ? $width : ($width / 2);
   }

   my $page = $self->_pdf->open_page(__pageno($opts->{page} // 'last'));
   my $text = $page->text;
   $text->position($x, $y);
   $text->font($font, $opts->{font_size});
   $text->text($content // '');

   return $self;
} ## end sub _op_add_text

sub _render_text ($self, $plain, $template, $crumbs) {
   return $plain                 if defined $plain;
   return $self->_tpr($template) if defined $template;
   return Template::Perlish::traverse($self->_data, $crumbs) // ''
     if defined $crumbs;
   return;
} ## end sub _render_text

sub _op_set_defaults ($self, $command) {
   my $defaults = $self->_defaults;
   while (my ($key, $value) = each $command->%*) {



( run in 0.571 second using v1.01-cache-2.11-cpan-71847e10f99 )