如何合并PDF文件(如果不可能则使用PS),使得每个文件都从奇数页开始?

18

我正在使用UNIX系统,希望将成千上万个PDF文件合并成一个文件以便打印。事先我不知道它们有多少页。

我想要双面打印,使得两个文件不在同一页上。

因此,我需要将合并的文件对齐,确保每个文件都从奇数页开始,并在下一页为偶数页时添加空白页。


相关链接:https://dev59.com/EG855IYBdhLWcg3wuG0M 我会使用 pdfjam - Martin Schröder
1
数千个文件会让任何命令行解决方案崩溃。那么,500个一批怎么样?否则,请测试测试测试。祝你好运。 - shellter
我希望a2ps可以被说服做到这一点。 - luser droog
9个回答

13

这是我使用的解决方案(基于@Dingo的基本原理,但对于PDF操作使用了更简单的方法):

  1. 创建一个只有一个空白页面的PDF文件

    首先,在某个地方创建一个只有一个空白页面的PDF文件(在我的情况下,它位于 /path/to/blank.pdf)。可以使用以下命令(来自此主题):

    touch blank.ps && ps2pdf blank.ps blank.pdf
    
  2. 运行Bash脚本

    接着,在包含所有PDF文件的目录中,我运行一个小脚本,将blank.pdf文件添加到每个奇数页码的PDF文件末尾:

  3. #!/bin/bash
    
    for f in *.pdf; do
      let npages=$(pdfinfo "$f"|grep 'Pages:'|awk '{print $2}')
      let modulo="($npages %2)"
      if [ $modulo -eq 1 ]; then
        pdftk "$f" "/path/to/blank.pdf" output "aligned_$f"
        # or
        # pdfunite "$f" "/path/to/blank.pdf" "aligned_$f"
      else
        cp "$f" "aligned_$f"
      fi
    done
    
  4. 合并结果

    现在,所有以 aligned_ 前缀的文件都有偶数页码,我可以使用以下命令将它们合并:

    pdftk aligned_*.pdf output result.pdf
    # or
    pdfunite aligned_*.pdf result.pdf
    

工具信息:

  • ps2pdf在大多数Linux发行版中的ghostscript
  • pdfinfopdfunite来自Poppler PDF渲染库(通常软件包名称为poppler-utilspoppler_utils
  • pdftk通常是它自己的包,即pdftk软件包

当我使用Nix时,nix-shell -p ghostscript poppler_utils会把我放在一个子shell中,并提供所有必需的命令。 - toraritte

4

如果您从另一个角度看待这个问题,它可能会更容易解决。

为了实现这一点,在打印时,将第二个pdf文件的第一页不附加到第一个pdf文件的最后一页上,更普遍地讲,后续pdf文件的第一页不能与前一个pdf文件的最后一页在同一张纸的背面打印。

您需要选择性地向具有奇数页数的pdf文件添加仅一个空白页。

我编写了一个简单的脚本,名为abbblankifneeded,您可以将其放在一个文件中,然后复制到/usr/bin/usr/local/bin中。

然后,在您存储pdf的文件夹中使用以下语法来调用:

for f in *.pdf; do addblankifneeded $f; done

该脚本会向具有奇数页数的pdf文件末尾添加一个空白页,跳过已经有偶数页数的pdf文件,然后将所有pdf文件合并成一个文件。

要求:pdftkpdfinfo

注意:根据您的bash环境,您可能需要将脚本第一行的sh解释器替换为bash解释器

#!/bin/sh
#script to add automatically blank page at the end of a pdf documents, if count of their pages is a not a module of 2 and then to join all pdfs into one
#
#  made by Dingo
#
# dokupuppylinux.co.cc
#
#http://pastebin.com/u/dingodog (my pastebin toolbox for pdf scripts)
#
filename=$1
altxlarg="`pdfinfo -box $filename| grep MediaBox | cut -d : -f2 | awk '{print $3 FS $4}'`"
echo "%PDF-1.4
%µí®û
3 0 obj
<<
/Length 0
>>
stream
endstream
endobj
4 0 obj
<<
/ProcSet [/PDF ]
/ExtGState <<
/GS1 1 0 R
>>
>>
endobj
5 0 obj
<<
/Type /Halftone
/HalftoneType 1
/HalftoneName (Default)
/Frequency 60
/Angle 45
/SpotFunction /Round
>>
endobj
1 0 obj
<<
/Type /ExtGState
/SA false
/OP false
/HT /Default
>>
endobj
2 0 obj
<<
/Type /Page
/Parent 7 0 R
/Resources 4 0 R
/Contents 3 0 R
>>
endobj
7 0 obj
<<
/Type /Pages
/Kids [2 0 R ]
/Count 1
/MediaBox [0 0 595 841]
>>
endobj
6 0 obj
<<
/Type /Catalog
/Pages 7 0 R
>>
endobj
8 0 obj
<<
/CreationDate (D:20110915222508)
/Producer (libgnomeprint Ver: 2.12.1)
>>
endobj
xref
0 9
0000000000 65535 f
0000000278 00000 n
0000000357 00000 n
0000000017 00000 n
0000000072 00000 n
0000000146 00000 n
0000000535 00000 n
0000000445 00000 n
0000000590 00000 n
trailer
<<
/Size 9
/Root 6 0 R
/Info 8 0 R
>>
startxref
688
%%EOF" | sed -e "s/595 841/$altxlarg/g">blank.pdf
pdftk blank.pdf output fixed.pdf
mv fixed.pdf blank.pdf
pages="`pdftk $filename dump_data | grep NumberOfPages | cut -d : -f2`"
if [ $(( $pages % 2 )) -eq 0 ]
    then echo "$filename has already a multiple of 2 pages ($pages ). Script will be skipped for this file" >>report.txt
    else
pdftk A=$filename B=blank.pdf cat A B output blankadded.pdf
mv blankadded.pdf $filename
pdffiles=`ls *.pdf | grep -v -e blank.pdf -e joinedtogether.pdf| xargs -n 1`;  pdftk $pdffiles cat output joinedtogether.pdf
fi
exit 0

非常好!很高兴看到如何处理PDF文件的示例。我很惊讶它是如此简单。祝大家好运。 - shellter

3
您可以使用PDFsam
  • 免费
  • 可在Microsoft Windows、Mac OS X和Linux上运行
  • 有便携版本(至少在Windows上)
  • 如果文档页数为奇数,可以在每个合并的文档后添加一个空白页

enter image description here


2

免责声明:以下提到的工具是本人编写。

sejda-console

这是一个免费且开源的命令行界面工具,用于执行 PDF 的合并或拆分等操作。 merge 命令有一个选项:

[--addBlanks]:如果合并后文档页数为奇数,则在每个文档之后添加一个空白页(可选)

既然您只需要打印 PDF,我假设您不关心文档的顺序。这是您可以使用的命令:

sejda-console merge -d /path/to/pdfs_to_merge -o /outputpath/merged_file.pdf --addBlanks

它可以从官方网站 sejda.org 下载。

sejda.com

这是一个由 Sejda 支持的 Web 应用程序,具有上述相同的功能,但通过 Web 界面实现。 您需要上传文件,因此,根据您的输入集的大小,可能不是适合您的正确解决方案。

如果选择 merge 命令并上传 PDF 文档,则必须选中复选框 Add blank page if odd page number 才能获得所需的行为。


1

这是最流行的解决方案的PowerShell版本,使用pdftk。我为Windows编写了此代码,但您可以在其他平台上使用PowerShell Core。

# install pdftk server if on windows
# https://www.pdflabs.com/tools/pdftk-server/

$blank_pdf_path = ".\blank.pdf"
$input_folder = ".\input\"
$aligned_folder = ".\aligned\"
$final_output_path = ".\result.pdf"

foreach($file in (Get-ChildItem $input_folder -Filter *.pdf))
{
    # easy but might break if pdfinfo output changes
    # takes 7th line with the "Page: 2" and matches only numbers
    (pdfinfo $file.FullName)[7] -match "(\d+)" | Out-Null

    $npages = $Matches[1]
    $modulo = $npages % 2

    if($modulo -eq 1)
    {
        $output_path = Join-Path $aligned_folder $file.Name
        pdftk $file.FullName $blank_pdf_path output $output_path
    }
    else
    {
        Copy-Item $file.FullName -Destination $aligned_folder
    }
}

$aligned_pdfs = Join-Path $aligned_folder "*.pdf"
pdftk $aligned_pdfs output $final_output_path

0

Martin 有了一个良好的开端。我更新了 PyPdf2 并进行了一些微调,例如按文件名排序输出。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from argparse import ArgumentParser
from glob import glob
from PyPDF2 import PdfFileReader, PdfFileWriter
import os.path
def merge(pdfpath, blank_filename, output_filename):

    with open(blank_filename, "rb") as f:
        blank = PdfFileReader(f)
        output = PdfFileWriter()

        filelist = sorted(glob(os.path.join(pdfpath,'*.pdf')))

        for pdffile in filelist:
            if pdffile == output_filename:
                continue
            print("Parse '%s'" % pdffile)

            document = PdfFileReader(open(pdffile, 'rb'))

            for i in range(document.getNumPages()):
                output.addPage(document.getPage(i))

            if document.getNumPages() % 2 == 1:
                output.addPage(blank.getPage(0))

            print("Add blank page to '%s' (had %i pages)" % (pdffile, document.getNumPages()))

        print("Start writing '%s'" % output_filename)
        with open(output_filename, "wb") as output_stream:
            output.write(output_stream)


if __name__ == "__main__":
    parser = ArgumentParser()

    # Add more options if you like
    parser.add_argument("-o", "--output", dest="output_filename", default="merged.pdf",
                      help="write merged PDF to FILE", metavar="FILE")
    parser.add_argument("-b", "--blank", dest="blank_filename", default="blank.pdf",
                      help="path to blank PDF file", metavar="FILE")
    parser.add_argument("-p", "--path", dest="path", default=".",
                      help="path of source PDF files")

    args = parser.parse_args()
    merge(args.path, args.blank_filename, args.output_filename)
`

0

这个方法对我有效。在macOS上使用了pdfcpu。 可以按照以下方式安装:

brew install pdfcpu

并且稍作修改来自https://dev59.com/m2kw5IYBdhLWcg3wi7Fz#12761103的代码。

#!/bin/bash
mkdir aligned
for f in *.pdf; do
  let npages=$(pdfcpu info "$f"|grep 'Page count:'|awk '{print $3}')
  let modulo="($npages %2)"
  if [ $modulo -eq 1 ]; then
    pdfcpu page insert -pages l -mode after "$f" "aligned/$f"
  else
    cp "$f" "aligned/$f"
  fi
done
pdfcpu merge merged-aligned.pdf aligned/*.pdf
rm -rf aligned

注意!它在当前目录中创建并删除“aligned”目录。因此,可以随意改进它以使其更安全使用。


0

Chris Lercher在https://dev59.com/m2kw5IYBdhLWcg3wi7Fz#12761103中的代码对我来说并不完全适用。我不知道这是因为我正在使用Cygwin/mintty,还是因为我必须使用qpdf而不是pdftk。以下是对我有效的代码:

#!/bin/bash

for f in *.pdf; do
  npages=$(pdfinfo "$f"|grep 'Pages:'|sed 's/[^0-9]*//g')
  modulo=$(($npages %2))
  if [ $modulo -eq 1 ]; then
    qpdf --empty --pages "$f" "path/to/blank.pdf" -- "aligned_$f"
  else
    cp "$f" "aligned_$f"
  fi
done

现在,所有的“aligned_”文件都有偶数页码,我可以使用qpdf将它们合并(感谢https://dev59.com/AXE95IYBdhLWcg3wAo9U#51080927)。
qpdf --verbose --empty --pages aligned_* -- all.pdf

下面是我使用的用于创建空白页面的https://unix.stackexchange.com/a/272878中的有用代码:

echo "" | ps2pdf -sPAPERSIZE=a4 - blank.pdf

0

准备工作

  1. 安装Python并确保您已经安装了pyPDF包。
  2. 创建一个带有单个空白的PDF文件,路径为/path/to/blank.pdf(我已经在这里创建了空白pdf页面)。
  3. 将其保存为pdfmerge.py在您的$PATH的任何目录中。(我不是Windows用户。在Linux下,这很简单。如果您遇到错误或者它可以正常工作,请告诉我。)
  4. 使pdfmerge.py可执行。

每次需要时

在只包含要合并的PDF文件的目录中运行uniprint.py

pdfmerge.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from argparse import ArgumentParser
from glob import glob
from pyPdf import PdfFileReader, PdfFileWriter

def merge(path, blank_filename, output_filename):
    blank = PdfFileReader(file(blank_filename, "rb"))
    output = PdfFileWriter()

    for pdffile in glob('*.pdf'):
        if pdffile == output_filename:
            continue
        print("Parse '%s'" % pdffile)
        document = PdfFileReader(open(pdffile, 'rb'))
        for i in range(document.getNumPages()):
            output.addPage(document.getPage(i))

        if document.getNumPages() % 2 == 1:
            output.addPage(blank.getPage(0))
            print("Add blank page to '%s' (had %i pages)" % (pdffile, document.getNumPages()))
    print("Start writing '%s'" % output_filename)
    output_stream = file(output_filename, "wb")
    output.write(output_stream)
    output_stream.close()

if __name__ == "__main__":
    parser = ArgumentParser()

    # Add more options if you like
    parser.add_argument("-o", "--output", dest="output_filename", default="merged.pdf",
                      help="write merged PDF to FILE", metavar="FILE")
    parser.add_argument("-b", "--blank", dest="blank_filename", default="blank.pdf",
                      help="path to blank PDF file", metavar="FILE")
    parser.add_argument("-p", "--path", dest="path", default=".",
                      help="path of source PDF files")

    args = parser.parse_args()
    merge(args.path, args.blank_filename, args.output_filename)

测试

请在Windows和Mac上测试并留下评论。

如果不能正常工作/需要改进,请始终留下评论。

在Linux上它可以正常工作。将3个PDF合并为一个200页的PDF只需不到一秒钟。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接