Python中搜索和替换PDF中的文本
Python中搜索和替换PDF中的文本
这个问题已经有了答案:
作为Python Web应用程序的一部分,我正在编写邮件合并软件。
我有一个称为letter.pdf
的模板,它是从一个MS Word文件生成的,并包含文本{name},其中居民的姓名将出现。 我还有一个约100名居民姓名的列表。
我想要做的是读取letter.pdf
,搜索\"{name}\"
,并用居民的姓名替换它(对于每个居民),然后将结果写入另一个PDF中。 然后,我想将所有这些PDF文件聚合到一个大PDF文件中(每封信一张纸),我的Web应用程序的用户将打印出来以创建他们的信件。
是否有任何Python库可以做到这一点? 我已经看过pdfrw和pdfminer,但我看不到它们能实现这一点的地方。
(NB:我还有MS Word文件,因此如果还有其他使用它而不是通过PDF进行的方法,那也可以完成工作。)
如果@Dmytrio的解决方案不改变最终PDF文件
Dymitrio的更新代码示例可以处理包含要更新的文本数据流的DecodedStreamObject和EncodedStreamObject,但是在使用不同于示例的文件时,无法更改PDF文本内容。
根据编辑3,来自如何使用Python替换PDF中的文本?:
在writer.addPage(page)
之前插入page [NameObject(“/Contents”)] = contents.decodedSelf
,我们强制pyPDF2更新页面对象的内容。
这样,我能够解决这个问题,并替换PDF文件中的文本。
最终代码应该像这样:
import os import argparse from PyPDF2 import PdfFileReader, PdfFileWriter from PyPDF2.generic import DecodedStreamObject, EncodedStreamObject, NameObject def replace_text(content, replacements = dict()): lines = content.splitlines() result = "" in_text = False for line in lines: if line == "BT": in_text = True elif line == "ET": in_text = False elif in_text: cmd = line[-2:] if cmd.lower() == 'tj': replaced_line = line for k, v in replacements.items(): replaced_line = replaced_line.replace(k, v) result += replaced_line + "\n" else: result += line + "\n" continue result += line + "\n" return result def process_data(object, replacements): data = object.getData() decoded_data = data.decode('utf-8') replaced_data = replace_text(decoded_data, replacements) encoded_data = replaced_data.encode('utf-8') if object.decodedSelf is not None: object.decodedSelf.setData(encoded_data) else: object.setData(encoded_data) if __name__ == "__main__": ap = argparse.ArgumentParser() ap.add_argument("-i", "--input", required=True, help="path to PDF document") args = vars(ap.parse_args()) in_file = args["input"] filename_base = in_file.replace(os.path.splitext(in_file)[1], "") # Provide replacements list that you need here replacements = { 'PDF': 'DOC'} pdf = PdfFileReader(in_file) writer = PdfFileWriter() for page_number in range(0, pdf.getNumPages()): page = pdf.getPage(page_number) contents = page.getContents() if isinstance(contents, DecodedStreamObject) or isinstance(contents, EncodedStreamObject): process_data(contents, replacements) elif len(contents) > 0: for obj in contents: if isinstance(obj, DecodedStreamObject) or isinstance(obj, EncodedStreamObject): streamObj = obj.getObject() process_data(streamObj, replacements) # Force content replacement page[NameObject("/Contents")] = contents.decodedSelf writer.addPage(page) with open(filename_base + ".result.pdf", 'wb') as out_file: writer.write(out_file)
重要提示:from PyPDF2.generic import NameObject
这可以通过使用 PyPDF2 包来完成。实现可能取决于原始 PDF 模板的结构。但如果模板足够稳定,并且不经常更改,替换代码就不应该是通用的,而是简单的。
我画了一个小草图,说明如何替换 PDF 文件 中的文本。它将所有出现的 PDF
标记替换为 DOC
。
import os import argparse from PyPDF2 import PdfFileReader, PdfFileWriter from PyPDF2.generic import DecodedStreamObject, EncodedStreamObject def replace_text(content, replacements = dict()): lines = content.splitlines() result = "" in_text = False for line in lines: if line == "BT": in_text = True elif line == "ET": in_text = False elif in_text: cmd = line[-2:] if cmd.lower() == 'tj': replaced_line = line for k, v in replacements.items(): replaced_line = replaced_line.replace(k, v) result += replaced_line + "\n" else: result += line + "\n" continue result += line + "\n" return result def process_data(object, replacements): data = object.getData() decoded_data = data.decode('utf-8') replaced_data = replace_text(decoded_data, replacements) encoded_data = replaced_data.encode('utf-8') if object.decodedSelf is not None: object.decodedSelf.setData(encoded_data) else: object.setData(encoded_data) if __name__ == "__main__": ap = argparse.ArgumentParser() ap.add_argument("-i", "--input", required=True, help="path to PDF document") args = vars(ap.parse_args()) in_file = args["input"] filename_base = in_file.replace(os.path.splitext(in_file)[1], "") # Provide replacements list that you need here replacements = { 'PDF': 'DOC'} pdf = PdfFileReader(in_file) writer = PdfFileWriter() for page_number in range(0, pdf.getNumPages()): page = pdf.getPage(page_number) contents = page.getContents() if isinstance(contents, DecodedStreamObject) or isinstance(contents, EncodedStreamObject): process_data(contents, replacements) elif len(contents) > 0: for obj in contents: if isinstance(obj, DecodedStreamObject) or isinstance(obj, EncodedStreamObject): streamObj = obj.getObject() process_data(streamObj, replacements) writer.addPage(page) with open(filename_base + ".result.pdf", 'wb') as out_file: writer.write(out_file)
结果如下
更新 2021-03-21:
更新了代码示例,以处理实际包含要更新文本的数据流的 DecodedStreamObject
和 EncodedStreamObject
。