我要处理通过“合并”多个 PDF 创建的单个 PDF。每个合并的 PDF 都有 PDF 部分开始显示的位置,并带有书签。
有没有办法通过带有脚本的书签自动拆分它?
我们只有用于指示部件的书签,而不是页码,因此我们需要从书签中推断出页码。最好有一个 Linux 工具。
我要处理通过“合并”多个 PDF 创建的单个 PDF。每个合并的 PDF 都有 PDF 部分开始显示的位置,并带有书签。
有没有办法通过带有脚本的书签自动拆分它?
我们只有用于指示部件的书签,而不是页码,因此我们需要从书签中推断出页码。最好有一个 Linux 工具。
pdftk 可用于拆分 PDF 文件并提取书签的页码。
要获取书签的页码,请执行
pdftk in.pdf dump_data
并使您的脚本从输出中读取页码。
然后使用
pdftk in.pdf cat A-B output out_A-B.pdf
将 A 到 B 的页面转换为 out_A-B.pdf。
脚本可能是这样的:
#!/bin/bash
infile=$1 # input pdf
outputprefix=$2
[ -e "$infile" -a -n "$outputprefix" ] || exit 1 # Invalid args
pagenumbers=( $(pdftk "$infile" dump_data | \
grep '^BookmarkPageNumber: ' | cut -f2 -d' ' | uniq)
end )
for ((i=0; i < ${#pagenumbers[@]} - 1; ++i)); do
a=${pagenumbers[i]} # start page number
b=${pagenumbers[i+1]} # end page number
[ "$b" = "end" ] || b=$[b-1]
pdftk "$infile" cat $a-$b output "${outputprefix}"_$a-$b.pdf
done
有一个用 Java 编写的名为Sejda的命令行工具,您可以在其中找到splitbybookmarks
完全符合您要求的命令。它是 Java,因此它在 Linux 上运行,并且作为一个命令行工具,您可以编写脚本来执行此操作。
免责声明
我是作者之一
你有类似pdf-split的程序可以为你做到这一点:
A-PDF Split 是一个非常简单、快速的桌面实用程序,可让您将任何 Acrobat pdf 文件拆分为更小的 pdf 文件。它在如何拆分文件以及如何唯一命名拆分的输出文件方面提供了完全的灵活性和用户控制。A-PDF Split 为您的大文件的拆分方式提供了多种选择 - 按页面、按书签和按奇数/偶数页。甚至您也可以提取或删除 PDF 文件的一部分。A-PDF Split 还提供高级定义的拆分,可以保存并在以后导入以用于重复的文件拆分任务。A-PDF Split 代表了文件拆分的终极灵活性,可满足各种需求。
A-PDF Split 适用于受密码保护的 pdf 文件,并且可以将各种 pdf 安全功能应用于拆分的输出文件。如果需要,您可以使用 A-PDF Merger 等实用程序将生成的拆分文件与其他 pdf 文件重新组合,以形成新的复合 pdf 文件。
A-PDF Split 不需要 Adobe Acrobat,并且可以生成与 Adobe Acrobat Reader 版本 5 及更高版本兼容的文档。
编辑*
如果您不想付费,还可以在此处找到一个免费的开源程序。
这是我用于该任务的一个小 Perl 程序。Perl 并不特别。它只是 pdftk 的包装器,用于解释其dump_data
输出以将其转换为要提取的页码:
#!perl
use v5.24;
use warnings;
use Data::Dumper;
use File::Path qw(make_path);
use File::Spec::Functions qw(catfile);
my $pdftk = '/usr/local/bin/pdftk';
my $file = $ARGV[0];
my $split_dir = $ENV{PDF_SPLIT_DIR} // 'pdf_splits';
die "Can't find $ARGV[0]\n" unless -e $file;
# Read the data that pdftk spits out.
open my $pdftk_fh, '-|', $pdftk, $file, 'dump_data';
my @chapters;
while( <$pdftk_fh> ) {
state $chapter = 0;
next unless /\ABookmark/;
if( /\ABookmarkBegin/ ) {
my( $title ) = <$pdftk_fh> =~ /\ABookmarkTitle:\s+(.+)/;
my( $level ) = <$pdftk_fh> =~ /\ABookmarkLevel:\s+(.+)/;
my( $page_number ) = <$pdftk_fh> =~ /\BookmarkPageNumber:\s+(.+)/;
# I only want to split on chapters, so I skip higher
# level numbers (higher means more nesting, 1 is lowest).
next unless $level == 1;
# If you have front matter (preface, etc) then this numbering
# will be off. Chapter 1 might be called Chapter 3.
push @chapters, {
title => $title,
start_page => $page_number,
chapter => $chapter++,
};
}
}
# The end page for one chapter is one before the start page for
# the next chapter. There might be some blank pages at the end
# of the split for PDFs where the next chapter needs to start on
# an odd page.
foreach my $i ( 0 .. $#chapters - 1 ) {
my $last_page = $chapters[$i+1]->{start_page} - 1;
$chapters[$i]->{last_page} = $last_page;
}
$chapters[$#chapters]->{last_page} = 'end';
make_path $split_dir;
foreach my $chapter ( @chapters ) {
my( $start, $end ) = $chapter->@{qw(start_page last_page)};
# slugify the title so use it as a filename
my $title = lc( $chapter->{title} =~ s/[^a-z]+/-/gri );
my $path = catfile( $split_dir, "$title.pdf" );
say "Outputting $path";
# Use pdftk to extract that part of the PDF
system $pdftk, $file, 'cat', "$start-$end", 'output', $path;
}