如何在 Python 中任意旋转、重新排列等 pdf 页面?

spr*_*aff 9 python pdf

我有一个“正常”的 input.pdf(许多页面的方向和方向都相同),我想创建一个可以任意重新排列输入页面的新 pdf

例如:

在此处输入图片说明

我只需要旋转和缩放。每个输入页面都将作为输出的某个组件完整呈现。我不需要摆弄文本、颜色、裁剪等。

在伪代码中,这些是我需要的所有功能:

in = open_pdf("input.pdf")
out = new_pdf ()

p = createpage (size)
p.add (in.get_page(123), origin=(0,100), scale=(0.5,0.5), angle=degrees(270))
p.add (...)

out.add(p)

out.save("output.pdf")
Run Code Online (Sandbox Code Playgroud)

我可以在 Python 中做到这一点吗?

如果不是 Python,还有其他一些对 linux 友好的脚本语言?

use*_*170 8

使用PyPDF2,您可以编写一个脚本来完成与您的伪代码非常相似的任务。

这是一些示例代码,使用同伦类型理论教科书的每晚构建作为输入:

#!/usr/bin/env python3
from PyPDF2 import PdfFileReader, PdfFileWriter

# matrix helper class

class AfMatrix:
    """ A matrix of a 2D affine transform. """

    __slots__ = ('__a', '__b', '__c', '__d', '__e', '__f')

    def __init__(self, a, b, c, d, e, f):
        self.__a = float(a)
        self.__b = float(b)
        self.__c = float(c)
        self.__d = float(d)
        self.__e = float(e)
        self.__f = float(f)

    def __iter__(self):
        yield self.__a
        yield self.__b
        yield self.__c
        yield self.__d
        yield self.__e
        yield self.__f

    def __hash__(self):
        return hash(tuple(self))

    def __eq__(self, other):
        return tuple(self) == tuple(other)

    @classmethod
    def compose(cls, *what):
        a, b, c, d, e, f = (
            1, 0,
            0, 1,
            0, 0,
        )

        for rhs in what:
            A, B, C, D, E, F = rhs
            a, b, c, d, e, f = (
                a * A + b * C,
                a * B + b * D,
                c * A + d * C,
                c * B + d * D,
                e * A + f * C + E,
                e * B + f * D + F,
            )

        return cls(
            a, b,
            c, d,
            e, f
        )

    @classmethod
    def translate(cls, x=0, y=0):
        return cls(
            1, 0,
            0, 1,
            x, y
        )

    def __takes_origin(func):
        def translated_func(cls, *args, origin=(0, 0), **kwargs):
            if origin == (0, 0):
                return func(cls, *args, **kwargs)
            return cls.compose(
                cls.translate(-origin[0], -origin[1]),
                func(cls, *args, **kwargs),
                cls.translate(origin[0], origin[1])
            )
        return translated_func

    @classmethod
    @__takes_origin
    def shear(cls, x=1, y=1):
        return cls(
            x, 0,
            0, y,
            0, 0
        )

    @classmethod
    @__takes_origin
    def rotate(cls, angle):
        from math import cos, sin, radians

        angle = radians(angle)
        C = cos(angle)
        S = sin(angle)

        return cls(
             C,  S,
            -S,  C,
             0,  0
        )

#

reader = PdfFileReader('hott-online-1272-ga50f9bd.pdf')
writer = PdfFileWriter()

ipgs = [reader.getPage(i) for i in range(8)]

# page 1

writer.addPage(ipgs[0])

# page 2

opg1src = ipgs[2:5]

opg1 = writer.addBlankPage(0, 0)

yaccum = 0
for ipg in opg1src:
    opg1.mergeTransformedPage(ipg, AfMatrix.compose(
        AfMatrix.rotate(90),
        AfMatrix.translate(x=ipg.mediaBox.getHeight(), y=yaccum)
    ), expand=True)
    yaccum += ipg.mediaBox.getWidth()

# page 3

opg2 = writer.addBlankPage(
    ipgs[6].mediaBox.getWidth(),
    ipgs[6].mediaBox.getHeight()
)

opg2.mergeTransformedPage(ipgs[6], (
    AfMatrix.shear(x=1/3)
), expand=True)

opg2.mergeTransformedPage(ipgs[7], AfMatrix.compose(
    AfMatrix.translate(
        x=-opg2.mediaBox.getWidth() / 8,
        y=-opg2.mediaBox.getHeight() / 8
    ),
    AfMatrix.rotate(-15, origin=(opg2.mediaBox.getWidth(), 0)),
    AfMatrix.shear(x=0.75, y=0.75, origin=(opg2.mediaBox.getWidth(), 0))
), expand=False)

# output

with open('sample.pdf', 'wb') as ostream:
    writer.write(ostream)
Run Code Online (Sandbox Code Playgroud)

这是输出:

HoTT 教科书的页面,由脚本转换。 第 1 页包含未修改的书籍封面。 第 2 页包含三个正面内容页面,逆时针旋转 90°,自下而上并排放置。 第 3 页包含目录的前两页,剪切和倾斜。

关于变换矩阵的注意事项:在 PDF 和 PostScript 中,X 坐标向右增长,Y 坐标向上增长,就像在数学自定义中一样(与计算机图形中的自定义不同,Y 坐标向下增长)。数学习惯不同,点被视为行向量而不是列向量,因此出现在矩阵乘法的左侧。这意味着矩阵变换是从左到右而不是从右到左组成的:首先应用最左边的操作。此外,为了使正角度旋转以逆时针方式出现(再次像数学习惯一样),上面的旋转矩阵似乎转置为其通常的形式。

转换页面时,注意掉出原页面页面边界的内容;在新页面上,它实际上可能会呈现。(我还没有找到解决方案。)