要实现这一点,您必须:
- 将您的 PDF 拆分成单独的页面;
- 将您的 PDF 页面转换为 SVG;
- 编辑您想要的页面
- 重新组合页面
此答案将省略第 3 步,因为这不是可编程的。
拆分 PDF
如果您不希望以编程方式拆分文档,现代方式将是使用stapler。在你最喜欢的外壳中:
stapler burst file.pdf
将生成{file_1.pdf,...,file_N.pdf},其中1...N 是PDF 页面。 Stapler 本身使用PyPDF2 并且用于拆分 PDF 文件的代码并不那么复杂。以下函数拆分文件并将各个页面保存在当前目录中。 (无耻地抄袭commands.py文件)
import math
import os
from PyPDF2 import PdfFileWriter, PdfFileReader
def split(filename):
with open(filename) as inputfp:
inputpdf = PdfFileReader(inputfp)
base, ext = os.path.splitext(os.path.basename(filename))
# Prefix the output template with zeros so that ordering is preserved
# (page 10 after page 09)
output_template = ''.join([
base,
'_',
'%0',
str(math.ceil(math.log10(inputpdf.getNumPages()))),
'd',
ext
])
for page in range(inputpdf.getNumPages()):
outputpdf = PdfFileWriter()
outputpdf.addPage(inputpdf.getPage(page))
outputname = output_template % (page + 1)
with open(outputname, 'wb') as fp:
outputpdf.write(fp)
将单个页面转换为 SVG
现在要将 PDF 转换为可编辑文件,我可能会使用 pdf2svg。
pdf2svg input.pdf output.svg
如果我们看一下pdf2svg.c文件,我们可以看到,原则上代码并没有那么复杂(假设输入文件名在filename变量中,输出文件名在outputname多变的)。下面是一个 Python 中的最小工作示例。它需要 pycairo 和 pypoppler 库:
import os
import cairo
import poppler
def convert(inputname, outputname):
# Convert the input file name to an URI to please poppler
uri = 'file://' + os.path.abspath(inputname)
pdffile = poppler.document_new_from_file(uri, None)
# We only have one page, since we split prior to converting. Get the page
page = pdffile.get_page(0)
# Get the page dimensions
width, height = page.get_size()
# Open the SVG file to write on
surface = cairo.SVGSurface(outputname, width, height)
context = cairo.Context(surface)
# Now we finally can render the PDF to SVG
page.render_for_printing(context)
context.show_page()
此时您应该拥有一个所有文本都已转换为路径的 SVG,并且能够使用 Inkscape 进行编辑而不会出现渲染问题。
结合步骤 1 和 2
您可以在 for 循环中调用 pdf2svg 来执行此操作。但是您需要事先知道页数。下面的代码计算了页数,并一步完成了转换。它只需要 pycairo 和 pypoppler:
import os, math
import cairo
import poppler
def convert(inputname, base=None):
'''Converts a multi-page PDF to multiple SVG files.
:param inputname: Name of the PDF to be converted
:param base: Base name for the SVG files (optional)
'''
if base is None:
base, ext = os.path.splitext(os.path.basename(inputname))
# Convert the input file name to an URI to please poppler
uri = 'file://' + os.path.abspath(inputname)
pdffile = poppler.document_new_from_file(uri, None)
pages = pdffile.get_n_pages()
# Prefix the output template with zeros so that ordering is preserved
# (page 10 after page 09)
output_template = ''.join([
base,
'_',
'%0',
str(math.ceil(math.log10(pages))),
'd',
'.svg'
])
# Iterate over all pages
for nthpage in range(pages):
page = pdffile.get_page(nthpage)
# Output file name based on template
outputname = output_template % (nthpage + 1)
# Get the page dimensions
width, height = page.get_size()
# Open the SVG file to write on
surface = cairo.SVGSurface(outputname, width, height)
context = cairo.Context(surface)
# Now we finally can render the PDF to SVG
page.render_for_printing(context)
context.show_page()
# Free some memory
surface.finish()
将 SVG 组装成一个 PDF
要重新组装,您可以使用成对的 inkscape / 订书机手动转换文件。但是编写执行此操作的代码并不难。下面的代码使用 rsvg 和 cairo。从 SVG 转换并将所有内容合并为一个 PDF:
import rsvg
import cairo
def convert_merge(inputfiles, outputname):
# We have to create a PDF surface and inform a size. The size is
# irrelevant, though, as we will define the sizes of each page
# individually.
outputsurface = cairo.PDFSurface(outputname, 1, 1)
outputcontext = cairo.Context(outputsurface)
for inputfile in inputfiles:
# Open the SVG
svg = rsvg.Handle(file=inputfile)
# Set the size of the page itself
outputsurface.set_size(svg.props.width, svg.props.height)
# Draw on the PDF
svg.render_cairo(outputcontext)
# Finish the page and start a new one
outputcontext.show_page()
# Free some memory
outputsurface.finish()
PS:应该可以使用命令pdftocairo,但是好像没有调用render_for_printing(),这使得输出的SVG保持了字体信息。