裁剪 PDF 内容

Crop PDF Content

我有一个 pdf 文件想要拼版。它有 8.5x11" 页面、媒体框和裁剪框。我希望 pdf 有 17x11" 页面,通过合并相邻页面。不幸的是,大多数页面的内容要么完全在裁剪框之外,要么跨越裁剪框。因为每个页面只能有一个流和裁剪框,所以在拼版时,重叠的内容会变得可见。这很糟糕。

我不想栅格化我的 pdf,因为那样会提前修复 DPI。所以我不会考虑将页面导出为图像,附加图像(imagemagick),然后将这些配对图像嵌入到新的 pdf 中。

我在 postscript 中也遇到了问题 - 在 pdf->ps->pdf 转换过程中透明度、字体光栅化和其他视觉故障问题。

答案应该是可编写脚本的。

到目前为止我已经尝试过:

问题“Ghostscript removes content outside the crop box?”表明 ghostscript 的 pdfwrite 模块在生成输出 pdf 文件时,将根据裁剪框光栅化和裁剪内容。所以我只需要通过 ghostscript 的 pdfwrite 模块来传输我的 pdf。不幸的是,这不起作用。

当我尝试通过 evince 将 pdf 打印到另一个 pdf 时,我正要放弃。它工作得很好——裁剪框内的文本和矢量元素没有被栅格化,裁剪框外的元素被删除(我还没有测试跨界元素)。质量高分辨率(页面大小)且外观相同。事实上,除了元数据之外,一切似乎都一样。

所以:

如何访问它?

我认为 cup 的 pdftopdf 二进制文件可能会提供此功能。我在调用外部二进制文件时没有任何问题....但不知道如何使用 pdftopdf.

编辑: Link to test pdf。它包含光栅、矢量和文本项 - 一些被部分透明的项目部分遮挡 - 跨越并邻接相邻页面。再一次,通过 cups 打印此 PDF 似乎会裁剪裁剪框外的所有内容。然而,在 inkscape 中打开过滤后的 pdf 显示页外项目被单独屏蔽,没有被裁剪 - 除了文本,它被修剪。

Ghostscript 和 pdfwrite 设备通常不会栅格化输入 PDF 文件的内容(需要注意的是涉及透明输入和输出 < PDF 1.4 的情况)。

完全裁剪掉的对象不会保留到输出中。

所以简短的回答是,使用 Ghostscript 和 pdfwrite 设备这应该是完全可行的,其优点是可以在单个操作中拼版页面。我确实有一个关于类似情况下的裁剪(反向拼版)的公开错误报告,但还没有时间解决它。

请注意,Ghostscript 通常使用 MediaBox 作为剪辑区域,如果要使用 CropBox,则需要在命令行中添加 -dUseCropBox

诀窍是使用 Form XObjects 在一个页面中强加多个页面。 Form XObjects 可以引用整个 PDF ,并保持独立的剪辑。 PyPDF2 不支持 Form XObjects,因此合并统一了所有输入页面的流,以便它们共享输出页面的 clip/media 框。我已经成功地使用了 pdflatex 和 pdfrw (python) - 测试程序在下面内联。由于 Form XObjects 派生自类似的 postscript level 2 功能,如 it should be possible to achieve the same goal in ghostscript using "page clips". In fact , but it appears horrendously complicated. Combined with the font rasterization issues of poppler's pdftops 所建议(即使兼容级别 > 1.4),我已经放弃了 ghostscript 方法。

源自 的 Latex 脚本。需要 pdflatex:

\documentclass{article}
\usepackage{pdfpages}
\usepackage[paperwidth=8.5in, paperheight=11in]{geometry}
\usepackage[multidot]{grffile}
\pagestyle{plain}

\begin{document}
    \setlength\voffset{+0.0in}
    \setlength\hoffset{+0.0in}

    \includepdf[ noautoscale=true
               , frame=false
               , pages={1}
               ]
               {<file.pdf>}

    \eject \paperwidth=17in \pdfpagewidth=17in \paperheight=11in \pdfpageheight=11in 

    \includepdf[ nup=2x1
               , noautoscale=true
               , frame=false
               , pages={2-,}
               ]
               {<file.pdf>}
\end{document}

pdfrw(python 脚本)源自 pdfrw:examples:booklet。要求 pdfrw >= 0.2:

#!/usr/bin/env python3

# Copyright:
#   Yclept Nemo
#       2016
# License:
#   GPLv3

import itertools
import argparse
import pdfrw

# from itertool recipes in the python documentation
def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return itertools.zip_longest(*args, fillvalue=fillvalue)

def pagemerge(page, *pages):
    merged = pdfrw.PageMerge() + page
    for page in reversed(list(itertools.takewhile(lambda i: i is not None, reversed(pages)))):
        merged = merged + page
        merged[-1].x = merged[-2].x + merged[-2].w
    return merged.render()

parser = argparse.ArgumentParser(description='Impose PDF files using Form XOBjects')

parser.add_argument\
    ( "source"
    , help="PDF, source path"
    , type=pdfrw.PdfReader
    )
parser.add_argument\
    ( "-s", "--spacer"
    , help="PDF, spacer path"
    , type=lambda fp: next(iter(pdfrw.PdfReader(fp).pages), None)
    )
parser.add_argument\
    ( "target"
    , help="PDF, target path"
    )

args = parser.parse_args()

pages = args.source.pages[:1]

for pair in grouper(args.source.pages[1:], 2):
    assert pair[0] is not None
    pages.append(pagemerge(pair[0], args.spacer, pair[1]))

# include metadata in target
target = pdfrw.PdfWriter()
target.addpages(pages)
target.trailer.Info = args.source.Info
target.write(args.target)

pdfrw 0.2 的一些特性:

  • 请注意,操作 +=appendextend 未为 pdfrw.PageMerge 定义,即使它的行为类似于列表。此外 + 的作用类似于 +=,因为它修改了左侧对象。