简体   繁体   中英

How to use PDF::API2 to merge several pdfs into one with Perl?

I have lots of pdf documents to merge together, so I wrote this code to do it. It works for the case where I only have two pdf documents to merge, but if I give it more than two, the extra documents come out garbled. Can you help me find what's wrong?

#!/usr/bin/perl

use PDF::API2;
use List::Util qw( reduce );


# Given two pdfs and a page number, appends the given page of the second pdf to the first pdf
sub append_page_to_pdf {
    my ( $pdf1, $pdf2, $pg ) = @_;
    $pdf1->importpage( $pdf2, $pg );
}

# Given two pdfs, appends the second to the first.  Closes pdf2
sub merge_2_pdfs {
    my ($pdf1, $pdf2) = @_;
    map &append_page_to_pdf( $pdf1, $pdf2, $_ ), 1..$pdf2->pages;
    $pdf2->end;
    return $pdf1;
}

# does what it says
sub open_pdf {
    my $file = $_[0];
    my $pdf = PDF::API2->open( $file );
    print "Opened pdf ( $file )\n";
    return $pdf;
}

# reduces merge_2_pdfs over an array of pdfs
sub merge_pdfs {
    my @files = @_;
    my $starting_filename = shift @files;
    my $start_pdf = &open_pdf( $starting_filename );
    my $final_pdf = reduce { &merge_2_pdfs( $a, &open_pdf( $b ) ) } $start_pdf, @files;
    return $final_pdf;
}

# Get the arguments ie save_name, file1, file2, file3, ...
my @files = @ARGV;
my $save_name = shift @files;
my $save = &merge_pdfs( @files );
$save->saveas( $save_name );

The actual problem in your code is because you shift one of the files off before you merge them.

my $save_name = shift @files; 
# which should be 
my $save_name = $files[0];

Otherwise, the code actually works, and I didn't find anything garbled.

A few tips:

  1. use strict and use warnings

  2. The general practice now is to omit the & in your subroutine calls. See here for exceptions to that rule.

  3. In this case, the subroutines make your code verbose, which made it harder to follow. Here's something more concise.

     use strict; use warnings; use List::Util 'reduce'; use PDF::API2; my $new = reduce { $a->importpage($b, $_) foreach 1 .. $b->pages; $a } map { PDF::API2->open($_) } @ARGV; $new->saveas('new.pdf'); 
  4. A simple loop is a little more straighforward to read than using reduce .

     use PDF::API2; my $new = PDF::API2->new; foreach my $filename (@ARGV) { my $pdf = PDF::API2->open($filename); $new->importpage($pdf, $_) foreach 1 .. $pdf->pages; } $new->saveas('new.pdf'); 

PDF::Reuse .

prFile('myFile.pdf');
for my $pdf (@PDFS) {
    prDoc($pdf);
}
prEnd();

Another possibility is my library, CAM::PDF .

my $pdf1 = CAM::PDF->new($file1) or die;
my $pdf2 = CAM::PDF->new($file2) or die;
my $pdf3 = CAM::PDF->new($file3) or die;
$pdf1->appendPDF($pdf2);
$pdf1->appendPDF($pdf3);
$pdf1->cleanoutput($outfile);

or wrap it in a loop over @ARGV perhaps. For just two PDFs, I have a simple cmdline wrapper to do the same:

appendpdf.pl file1.pdf file2.pdf out.pdf

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM