使用 Ghostscript 分割 PDF

Posted

技术标签:

【中文标题】使用 Ghostscript 分割 PDF【英文标题】:Splitting a PDF with Ghostscript 【发布时间】:2012-04-30 23:57:07 【问题描述】:

我尝试使用 Ghostscript 拆分多页 PDF,我在更多网站甚至ghostscript.com 上找到了相同的解决方案,即:

gs -sDEVICE=pdfwrite -dSAFER -o outname.%d.pdf input.pdf

但这似乎对我不起作用,因为它会生成一个文件,其中 所有页面,名称为 outname.1.pdf.

当我添加开始页面和结束页面时,它工作正常,但我 希望它在不知道这些参数的情况下工作。

在 gs-devel 存档中,我找到了一个解决方案: http://ghostscript.com/pipermail/gs-devel/2009-April/008310.html-- 但我想在没有pdf_info 的情况下这样做。

当我使用不同的设备时,例如pswrite,但相同 参数,它可以正常工作,生成尽可能多的 ps 文件,就像我的 input.pdf 包含。

使用pdfwrite时这正常吗?我是不是做错了什么?

【问题讨论】:

【参考方案1】:

我发现 Weimer 先生编写的这个脚本非常有用:

#!/bin/sh
#
# pdfsplit [input.pdf] [first_page] [last_page] [output.pdf] 
#
# Example: pdfsplit big_file.pdf 10 20 pages_ten_to_twenty.pdf
#
# written by: Westley Weimer, Wed Mar 19 17:58:09 EDT 2008
#
# The trick: ghostscript (gs) will do PDF splitting for you, it's just not
# obvious and the required defines are not listed in the manual page. 

if [ $# -lt 4 ] 
then
        echo "Usage: pdfsplit input.pdf first_page last_page output.pdf"
        exit 1
fi
gs -dNOPAUSE -dQUIET -dBATCH -sOutputFile="$4" -dFirstPage=$2 -dLastPage=$3 -sDEVICE=pdfwrite "$1"

来源:http://www.cs.virginia.edu/~weimer/pdfsplit/pdfsplit

将其保存为pdfsplit.sh,看看奇迹发生了。

PDFSAM 也可以完成这项工作。适用于 Windows 和 Mac。

【讨论】:

太棒了。我没有 pdftk 和 psselect 会失去一些 pdf 质量,但不是这个。 感谢您提供指向 -dFirstPage=... 和 -dLastPage=... 参数的指针!【参考方案2】:

您看到的是“正常”行为:Ghostscript 的pdfwrite 输出设备的当前版本不支持此功能。 Use.htm 中也记录了这一点(诚然,有些模糊):

“请注意,并非所有设备都支持每个文件一页的功能......”

我似乎记得一位 Ghostscript 开发人员在 IRC 上提到,他们可能在未来的某个版本中将此功能添加到 pdfwrite,但这似乎需要一些主要的代码重写,这就是为什么他们还没做...


更新: 正如 Gordon 的评论已经暗示的那样,截至 version 9.06(2012 年 7 月 31 日发布),Ghostscript 现在支持问题中引用的命令行也适用于 pdfwrite。 (Gordon 一定是在 9.05 中发现了对此的非官方支持,或者他从尚未标记为 9.06 的预发布源编译了自己的可执行文件。

【讨论】:

是的,我读过这一行,但我的短语“正常行为”想要表示“pdfwrite 是可能不支持此功能的人之一吗?”你记住这个 IRC 对我来说没问题,谢谢。 对于在搜索中找到此答案的人:从 9.05 开始,使用 OP 的命令,每个文件一页对我有效。 @Gordon:支持-o out_%d.pdf 语法(将多页 PDF 拆分为每页的单个文件)在 9.06 中成为正式版本。我已经在其他答案中暗示了这一点(例如 Split multi page PDF file into single pages)。我忘了更新这个答案。感谢您的提示。【参考方案3】:
 #!/bin/bash
#where $1 is the input filename

ournum=`gs -q -dNODISPLAY -c "("$1") (r) file runpdfbegin pdfpagecount = quit" 2>/dev/null`
echo "Processing $ournum pages"
counter=1
while [ $counter -le $ournum ] ; do
    newname=`echo $1 | sed -e s/\.pdf//g`
    reallynewname=$newname-$counter.pdf
    counterplus=$((counter+1))
    # make the individual pdf page
    yes | gs -dBATCH -sOutputFile="$reallynewname" -dFirstPage=$counter -dLastPage=$counter -sDEVICE=pdfwrite "$1" >& /dev/null
    counter=$counterplus
done

【讨论】:

【参考方案4】:

假设您安装了 Ghostscript,以下是 Windows 命令提示符的脚本(也可用于拖放):

@echo off
chcp 65001
setlocal enabledelayedexpansion

rem Customize or remove this line if you already have Ghostscript folders in your system PATH
set path=C:\Program Files\gs\gs9.22\lib;C:\Program Files\gs\gs9.22\bin;%path%

:start

echo Splitting "%~n1%~x1" into standalone single pages...
cd %~d1%~p1
rem getting number of pages of PDF with GhostScript
for /f "usebackq delims=" %%a in (`gswin64c -q -dNODISPLAY -c "(%~n1%~x1) (r) file runpdfbegin pdfpagecount = quit"`) do set "numpages=%%a"

for /L %%n in (1,1,%numpages%) do (
echo Extracting page %%n of %numpages%...
set "x=00%%n"
set "x=!x:~-3!"
gswin64c.exe -dNumRenderingThreads=2 -dBATCH -dNOPAUSE -dQUIET -dFirstPage=%%n -dLastPage=%%n -sDEVICE=pdfwrite -sOutputFile="%~d1%~p1%~n1-!x!.pdf" "%1"
)

shift
if NOT x%1==x goto start

pause

将此脚本命名为split PDF.bat 之类的名称并将其放在您的桌面上。将一个(甚至更多)多页 PDF 拖放到其上,它将为 PDF 的每一页创建一个独立的 PDF 文件,并在名称后附加后缀 -001-002 等以区分页面。

如果您的系统 PATH 环境变量中已有 Ghostscript 文件夹,您可能需要自定义(使用相关的 Ghostscript 版本)或删除 set path=... 行。

它适用于带有 Ghostscript 9.22 的 Windows 10。请参阅 cmets 以使其与 Ghostscript 9.50+ 一起使用。

享受吧。

【讨论】:

+1 用于获取 GS 的页数,干得好!如果有人想在 linux/macOS 上获取页数,请使用 gs -q -dNODISPLAY -c "(../escaped\ file \name.pdf) (r) file runpdfbegin pdfpagecount = quit" 非常有帮助。可以与 GS 9.22 一起使用,但在某种程度上与(至少)9.50 和 9.52 不兼容。有人知道如何解决这个问题吗? @user18258 我不知道如何解决这个问题,但无论如何我发现使用另一个命令行工具在 Windows 上分割 PDF 文件更方便,sedja 控制台。这是一个拖放批次:codepile.net/pile/6lWv3wzY @mmj 感谢基于sedja 的代码!我将 GhostScript 用于许多“shell:sendto”任务,并且仍然对 9.52 兼容的解决方案感兴趣 - 尽管我知道您不会提供它。我在上面的基于 GS 的代码中发现了一个小错误(我仍在使用 GS 版本 9.27!):我认为 gswin64c.exe ... "%1" 应该是 gswin64c.exe ... %1,否则当路径包含空格时会出现问题。 @tstone-1 似乎对于 Ghostscript 9.50+,您必须添加 -dNOSAFER 选项(连同 -dNODISPLAY)。见:***.com/q/40156190【参考方案5】:

这是一个简单的 python 脚本:

#!/usr/bin/python3

import os

number_of_pages = 68
input_pdf = "abstracts_rev09.pdf"

for i in range(1, number_of_pages +1):
    os.system("gs -q -dBATCH -dNOPAUSE -sOutputFile=pagepage:04d.pdf"
              " -dFirstPage=page -dLastPage=page"
              " -sDEVICE=pdfwrite input_pdf"
              .format(page=i, input_pdf=input_pdf))

【讨论】:

【参考方案6】:

更新的答案仅依赖于 pdftk.exe,而不调用 Ghostscript

用户@mmj 提供的答案过去对我来说很好用,但不知何故在 GS 版本 9.20 和 9.50 之间的某个地方停止了工作。我也知道@Adobe 提供的解决方案。但是,我喜欢通过选择一个或多个文件并右键单击 → 发送到,从 Windows (10) 资源管理器中完成重复性任务。这是一个 Python 脚本(与 3.8 兼容),它使用 pdftk.exe(用 2.02 测试)来计算总页数并将全部提取到单个文件中。它应该接受多个 PDF 作为输入。确保 PATH 中有 Python 和 pdftk.exe

将此命名为extract-pdf-pages-py.cmd 并将其命名为shell:sendto

python %APPDATA%\Microsoft\Windows\SendTo\extract-pdf-pages-py.py %*

把下面的extract-pdf-pages-py.py放到同一个文件夹里:

#!/usr/bin/python3
# put as extract-pdf-pages-py.py to shell:sendto

import os
import subprocess
import re
import sys
import mimetypes


def is_tool(name):
    from shutil import which
    return which(name) is not None


if not is_tool('pdftk'):
    input('pdftk.exe not within PATH. Aborting...')
    raise SystemExit("pdftk.exe not within PATH.")

sys.argv.pop(0)

for j in range(len(sys.argv)):
    input_pdf = sys.argv[j]

    if 'application/pdf' not in mimetypes.guess_type(input_pdf):
        input(f"File input_pdf is not a PDF. Skipping...")
        continue

    savefile = input_pdf.rstrip('.pdf')

    numpages = subprocess.Popen(f"pdftk \"input_pdf\" dump_data", shell=True, stdout=subprocess.PIPE)
    output1 = str(numpages.communicate()[0])
    output2 = re.search("NumberOfPages: ([0-9]*)", output1)
    number_of_pages = int(output2.group(1))

    for i in range(1, number_of_pages + 1):
        os.system(f"pdftk \"input_pdf\" cat i output \"savefile\"i:04d.pdf")

我使用了来自this answer(@Adobe 编写的脚本)和that one (is_tool) 的代码。

【讨论】:

【参考方案7】:

gs 只接受升序页面。 为了从源代码中改组(即第 7、第 8、第 5)页,我为 ~/.bashrc 创建了函数:

function expdf

local str=""
local arr=($(echo $1 | tr "," "\n"))
#          splitting
for i in "$arr[@]";do
  gs -dBATCH -sDEVICE=pdfwrite -sPageList=$i -dNOPAUSE -sOutputFile=$i.tmp $2
#          reordering for combining
  str="$str $i.tmp"
done
#          combining to combine.pdf
gs -dNOPAUSE -sDEVICE=pdfwrite -sOUTPUTFILE=combine.pdf -dBATCH $str
#          removing temporary files
for i in "$arr[@]";do rm $i.tmp;done

使用示例:expdf 7-8,5 source.pdf

【讨论】:

以上是关于使用 Ghostscript 分割 PDF的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Ghostscript 和 Ghostscript .NET 通过嵌入 IIC 配置文件生成正确的 PDF/A

使用 GhostScript 打印 PDF

Ghostscript 找不到 Ghostscript 库 (libgs)

PDF 上的 Ghostscript 邮票图像

ImageMagick 中 GhostScript 的文件路径

使用 GhostScript 将图像转换为 PDF