【发布时间】:2011-02-05 19:23:32
【问题描述】:
我将处理通过“合并”多个 PDF 创建的单个 PDF。每个合并的 PDF 都有 PDF 部分开始显示的位置,并带有书签。
有没有什么方法可以通过书签和脚本自动拆分?
我们只有用于指示部件的书签,而不是页码,因此我们需要从书签中推断出页码。最好有一个 Linux 工具。
【问题讨论】:
标签: linux pdf pdf-generation tagged-pdf
我将处理通过“合并”多个 PDF 创建的单个 PDF。每个合并的 PDF 都有 PDF 部分开始显示的位置,并带有书签。
有没有什么方法可以通过书签和脚本自动拆分?
我们只有用于指示部件的书签,而不是页码,因此我们需要从书签中推断出页码。最好有一个 Linux 工具。
【问题讨论】:
标签: linux pdf pdf-generation tagged-pdf
您有像 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 及更高版本兼容。
编辑*
如果您不想付费,还可以找到一个免费的开源程序Here。
【讨论】:
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
【讨论】:
grep -A1 '^BookmarkLevel: 1' | grep '^BookmarkPageNumber: ' 仅获取顶级书签。不幸的是,所有较低级别的书签都会以这种方式丢失......
有一个用 Java 编写的名为 Sejda 的命令行工具,您可以在其中找到完全符合您要求的 splitbybookmarks 命令。它是 Java,所以它在 Linux 上运行,并且是一个命令行工具,您可以编写脚本来执行此操作。
免责声明
我是作者之一
【讨论】:
sejda-console 需要 Pro,即 2000 美元/年。当然不是我用例的选择。
这是我用于该任务的一个小 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;
}
【讨论】: