使用Python向现有PDF添加文本

164

我需要使用Python向现有的PDF中添加一些额外的文本,最佳方法是什么,我需要安装哪些额外的模块。

注意:理想情况下,我希望能够在Windows和Linux上运行此代码,但只在Linux上也可以。

编辑:pypdfReportLab看起来不错,但两者都无法允许我编辑现有的PDF,是否有其他选项?


PyPDF2允许您复制每一页并在顶部添加文本注释:https://pypdf2.readthedocs.io/en/latest/modules/AnnotationBuilder.html#PyPDF2.generic.AnnotationBuilder.text - Martin Thoma
9个回答

182

[Python 2.7]的示例:


from pyPdf import PdfFileWriter, PdfFileReader
import StringIO
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter

packet = StringIO.StringIO()
can = canvas.Canvas(packet, pagesize=letter)
can.drawString(10, 100, "Hello world")
can.save()

#move to the beginning of the StringIO buffer
packet.seek(0)

# create a new PDF with Reportlab
new_pdf = PdfFileReader(packet)
# read your existing PDF
existing_pdf = PdfFileReader(file("original.pdf", "rb"))
output = PdfFileWriter()
# add the "watermark" (which is the new pdf) on the existing page
page = existing_pdf.getPage(0)
page.mergePage(new_pdf.getPage(0))
output.addPage(page)
# finally, write "output" to a real file
outputStream = file("destination.pdf", "wb")
output.write(outputStream)
outputStream.close()

Python 3.x的示例:


from PyPDF2 import PdfFileWriter, PdfFileReader
import io
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter

packet = io.BytesIO()
can = canvas.Canvas(packet, pagesize=letter)
can.drawString(10, 100, "Hello world")
can.save()

#move to the beginning of the StringIO buffer
packet.seek(0)

# create a new PDF with Reportlab
new_pdf = PdfFileReader(packet)
# read your existing PDF
existing_pdf = PdfFileReader(open("original.pdf", "rb"))
output = PdfFileWriter()
# add the "watermark" (which is the new pdf) on the existing page
page = existing_pdf.pages[0]
page.merge_page(new_pdf.pages[0])
output.add_page(page)
# finally, write "output" to a real file
output_stream = open("destination.pdf", "wb")
output.write(output_stream)
output_stream.close()

17
对于Python3,应该使用io.BytesIO作为包,并且使用PyPDF2而不是未维护的pyPDF。很棒的答案! - Noufal Ibrahim
5
谢谢分享。它的效果非常好。有一个需要注意的地方:我认为使用open而不是file会更好。 - mitenka
3
注意:新文档仅包含原始文档的第一页!从existing_pdf复制其余页面到output很容易,示例代码只是没有这样做。 - alexis
1
@alexis:你要如何修改这段代码才能将内容放置在 PDF 的第二页上?我有一个使用两页的表格,但是我卡在第一页无法前进了。提前感谢你的帮助。 - DavidV
@alexis:我试过了,确实可以(昨天也可以,但我还有另一个问题要解决),但是它真的真的真的很慢。像每10秒钟才能处理1个文档。而我需要250个文档。你有什么想法如何解决这个问题吗?谢谢。 - DavidV
显示剩余5条评论

107

我知道这是一篇旧帖子,但我花了很长时间寻找解决方案。我发现只使用ReportLab和PyPDF就可以解决问题,因此我想分享一下我的方法:

  1. 使用PdfFileReader()读取您的PDF文件,我们将其称为input
  2. 使用ReportLab创建一个包含要添加文本的新PDF文件,并将其保存为字符串对象
  3. 使用PdfFileReader()读取该字符串对象,我们将其称为text
  4. 使用PdfFileWriter()创建一个新的PDF对象,我们将其称为output
  5. 遍历 input 并对每个要添加文本的页面应用.mergePage(*text*.getPage(0)),然后使用output.addPage()将修改后的页面添加到新文档中

这对于简单的文本添加非常有效。请参见PyPDF的示例以添加水印。

以下是回答下面问题的代码:

packet = StringIO.StringIO()
can = canvas.Canvas(packet, pagesize=letter)
<do something with canvas>
can.save()
packet.seek(0)
input = PdfFileReader(packet)

从这里,您可以将输入文件的页面与另一个文档合并。


1
我建议使用PyPDF2,因为它更加更新,同时请查看他们的示例代码:https://github.com/mstamy2/PyPDF2/blob/41d90b4d141d0b019d145748f53ea556efcb47d1/Sample_Code/basic_features.py - blaze
3
此代码将创建一个新的PDF文件,并将跳过所有元数据。因此它不会附加到现有的PDF中。 - Anton Kukoba

19

pdfrw可以让您从现有的PDF中读取页面并将其绘制到ReportLab画布上(类似于绘制图像)。在GitHub上的pdfrwexamples/rl1子目录中有相关示例。免责声明:我是pdfrw的作者。


说句实话,如果你开始遵循这个链接,你会发现更多的reportlab/pdfrw示例。我在那里回答了一个问题,基于重复的目标答案。 - Patrick Maupin

8

cpdf可以通过命令行完成工作。虽然不是Python(据我所知),但它可以帮助您。

cpdf -add-text "Line of text" input.pdf -o output .pdf

3
在使用cpdf之前,请仔细检查其许可证 - 它不是开源的。 - Tim Small

7

借鉴David Dehghan回答中的方法,以下代码适用于Python 2.7.13:

from PyPDF2 import PdfFileWriter, PdfFileReader, PdfFileMerger

import StringIO

from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter

packet = StringIO.StringIO()
# create a new PDF with Reportlab
can = canvas.Canvas(packet, pagesize=letter)
can.drawString(290, 720, "Hello world")
can.save()

#move to the beginning of the StringIO buffer
packet.seek(0)
new_pdf = PdfFileReader(packet)
# read your existing PDF
existing_pdf = PdfFileReader("original.pdf")
output = PdfFileWriter()
# add the "watermark" (which is the new pdf) on the existing page
page = existing_pdf.getPage(0)
page.mergePage(new_pdf.getPage(0))
output.addPage(page)
# finally, write "output" to a real file
outputStream = open("destination.pdf", "wb")
output.write(outputStream)
outputStream.close()

1
如果现有的PDF文件有多页,您如何确保输出的文件具有相同数量的页面,唯一的区别是编辑过的页面?我希望有一种更简单的方法,而不是制作奇怪的循环。 - West
PyPDF2已经被弃用,请使用pypdf: https://pypdf.readthedocs.io/en/stable/ - undefined

4

截至撰写日期,PyPDF2已经停用了PdfFileReader、PdfFileWriter和其他一些方法,并将其更改为不同的名称和方法。还直接将getPage()方法更改为PdfReader的属性。

以下是一个非常简单的类来向现有的pdf文件添加文本: (使用示例在末尾演示)

from PyPDF2 import PdfWriter, PdfReader, Transformation
import io
from reportlab.pdfgen.canvas import Canvas

class GenerateFromTemplate:
    def __init__(self,template):
        self.template_pdf = PdfReader(open(template, "rb"))
        self.template_page= self.template_pdf.pages[0]

        self.packet = io.BytesIO()
        self.c = Canvas(self.packet,pagesize=(self.template_page.mediabox.width,self.template_page.mediabox.height))

    
    def addText(self,text,point):
        self.c.drawString(point[0],point[1],text)

    def merge(self):
        self.c.save()
        self.packet.seek(0)
        result_pdf = PdfReader(self.packet)
        result = result_pdf.pages[0]

        self.output = PdfWriter()

        op = Transformation().rotate(0).translate(tx=0, ty=0)
        result.add_transformation(op)
        self.template_page.merge_page(result)
        self.output.add_page(self.template_page)
    
    def generate(self,dest):
        outputStream = open(dest,"wb")
        self.output.write(outputStream)
        outputStream.close()

"""
Use as:
gen = GenerateFromTemplate("template.pdf")
gen.addText("Hello!",(100,200))
gen.addText("PDF!",(100,300))
gen.merge()
gen.generate("Output.pdf")
"""

希望这能有所帮助。

1

不要使用mergePage,因为它可能无法处理某些PDF文件。你应该使用mergeRotatedTranslatedPage。

from PyPDF2 import PdfFileWriter, PdfFileReader
import io
from reportlab.pdfgen.canvas import Canvas

page_to_merge = 0 #Refers to the First page of PDF 
xcoor = 250 #To be changed according to your pdf
ycoor = 650 #To be changed according to your pdf

input_pdf = PdfFileReader(open("Source.pdf", "rb"))
page_count = input_pdf.getNumPages()
inputpdf_page_to_be_merged = input_pdf.getPage(page_to_merge)

packet = io.BytesIO()
c = Canvas(packet,pagesize=(inputpdf_page_to_be_merged.mediaBox.getWidth(),inputpdf_page_to_be_merged.mediaBox.getHeight()))
c.drawString(xcoor,ycoor,"Hello World")
c.save()
packet.seek(0)

overlay_pdf = PdfFileReader(packet)
overlay = overlay_pdf.getPage(0)

output = PdfFileWriter()

for PAGE in range(page_count):
    if PAGE == page_to_merge:
        inputpdf_page_to_be_merged.mergeRotatedTranslatedPage(overlay, 
                inputpdf_page_to_be_merged.get('/Rotate') or 0, 
                overlay.mediaBox.getWidth()/2, overlay.mediaBox.getWidth()/2)
        output.addPage(inputpdf_page_to_be_merged)
    
    else:
        Page_in_pdf = input_pdf.getPage(PAGE)
        output.addPage(Page_in_pdf)

outputStream = open("destination.pdf", "wb")
output.write(outputStream)
outputStream.close()

这个答案中使用的PyPDF2版本是什么? - thinker3
1
@thinker3,pypdf2的版本是1.26.0。 - ConMan77

-2

这篇白皮书看起来不错,但是代码方面有点不足,而且我也没有资源去自己实现一个完整的PDF框架!;) - Frozenskys

-3

你可以尝试将问题分解成将PDF转换为可编辑格式,编写更改,然后再将其转换回PDF。我不知道是否有直接编辑PDF的库,但是例如DOC和PDF之间有很多转换器。


2
问题在于我只有PDF格式的源代码(来自第三方),而通过 PDF -> DOC -> PDF 的转换将会损失很多内容。另外,我需要在Linux上运行此代码,因此DOC可能不是最佳选择。 - Frozenskys
我认为Adobe保持PDF编辑功能相当封闭和专有,以便他们可以销售更好版本的Acrobat许可证。也许你可以找到一种方法来自动化使用Acrobat Pro进行编辑,使用某种宏接口。 - aehlke
如果您要写入的部分是表单字段,则有XML接口可用于编辑它们-否则我找不到任何内容。 - aehlke
不,我只想在每个页面上添加几行文本。 - Frozenskys

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