As a self-appointed Unix guru ;-) 😉 and in accordance with StackExchange's roots, I ended up using command-line tools like "pdftk" and "perl" to solve this problem.
On an Apple Mac:
- install "pdftk" command-line tool (directly or via MacPorts)
install perl
- copy this script into a text file and run it with a path to the Image File Wrapper PDF as argument.
#!/usr/bin/perl
# Splits a PDF file according to its bookmarks (aka. table of contents) into
# several smaller files. Uses "pdftk".
# V1.0
# Redistribution and use in source and binary forms, with or without modification, are permitted.
# This software is provided "as is" and any express or implied warranties, including,
# but not limited to, the implied warranties of merchantability and fitness for a
# particular purpose are disclaimed. In no event shall the copyright holder or
# contributors be liable for any direct, indirect, incidental, special, exemplary, or
# consequential damages arising in any way out of the use of this software.
use strict;
use warnings;
$0 =~ s(^.*/)();
sub usage {
print "usage: $0 [-n] <filewrapper.pdf>\n";
print " -n dry run\n";
exit(0);
}
sub get_bookmarks { # ($filename, \%bookmarks, \$number_of_pages)
my $filename = shift;
my $ref_bookmarks = shift;
my $ref_max = shift;
open(PIPE, "-|", "pdftk", $filename, "dump_data") || return 1;
chomp(my @lines = <PIPE>);
close(PIPE);
return 2 if ($?);
#NumberOfPages: 163
#BookmarkBegin
#BookmarkTitle: 2012-10-09 Non-Final Rejection
#BookmarkLevel: 1
#BookmarkPageNumber: 1
#BookmarkBegin
#BookmarkTitle: 2012-10-09 List of references cited by examiner
#BookmarkLevel: 1
#BookmarkPageNumber: 20
%$ref_bookmarks = ();
my $page = "";
my $title = "";
$$ref_max = 0;
foreach my $line (@lines) {
my @fields = split(/:\s+/, $line, 2);
if ($fields[0] eq "BookmarkBegin") {
$page = $title = "";
} elsif ($fields[0] eq "BookmarkTitle") {
$title = $fields[1];
} elsif ($fields[0] eq "BookmarkPageNumber") {
$page = $fields[1];
} elsif ($fields[0] eq "NumberOfPages") {
$$ref_max = $fields[1];
}
if ($page ne "" && $page > 0 && $title ne "") {
$$ref_bookmarks{ $page } = $title;
$page = $title = "";
}
}
}
my $dry_run = 0;
if ($ARGV[0] eq "-n") {
$dry_run = 1;
shift(@ARGV);
}
if (@ARGV != 1) {
print "Error: check commandline!\n";
usage();
}
my $filename = $ARGV[0];
if (! -r $filename) {
die ("Error: Could not open '$filename'.\n");
}
my %bookmarks = ();
my $max = 0;
my $error = get_bookmarks($filename, \%bookmarks, \$max);
if ($error) {
die("Error: Could not run 'pdftk'.\n") if ($error == 1);
die("Error: pdftk reported an error. Aborting.\n");
}
print "Processing " . $max . " pages...\n";
my @pages_sorted = sort {$b <=> $a} keys %bookmarks; # reverse sort
if (@pages_sorted == 0) {
print "No table of contents found.\n";
exit(0);
}
my $begin = "";
my $end = "end";
$error = 0;
my @new_files = ();
for (my $i = 0; $i < @pages_sorted; $i++) {
$begin = $pages_sorted[$i];
my($date, $new_filename) = split (/\s+/, $bookmarks{ $begin }, 2);
# sanitize filename
# add characters which your filesystem dislikes into [ ] brackets
$new_filename =~ s([/;\\])(_)g;
$new_filename = $date . " " . sprintf("%03d", $i + 1) . " " . $new_filename . ".pdf";
push(@new_files, $new_filename);
my @cmd = ( "pdftk", $filename, "cat", $begin . "-" . $end, "output", $new_filename );
$end = $begin - 1;
if ($dry_run) {
print join(" ", @cmd) . "\n";
} else {
print $bookmarks{ $begin } . "\n";
system @cmd;
if ($?) {
$error++;
} else {
}
}
}
if ($error) {
die("Errors occurred during processing of $error file(s).\n");
} elsif (! $dry_run) {
print "Checking for completeness...";
my $page_count = 0;
foreach my $pdf (@new_files) {
my $c = 0;
get_bookmarks($pdf, {} , \$c );
$page_count += $c;
}
if ($page_count == $max) {
print "No errors.\nCreated " . @pages_sorted . " PDF files containing a total of $page_count pages.\n";
} else {
print "\n";
print "Error: All documents have been extracted but the total number of pages\n";
print " in all extracted documents is $page_count and does not match the number\n";
print " of $max pages in the source document '$filename'.\n";
}
}