如何使用Python替换PDF中的文本?
如何使用Python替换PDF中的文本?
我从另一个帖子这里拿到了使用PyPDF2库解析和替换PDF文本的代码。帖子中给出的示例PDF被解析为PyPDF2.generic.DecodedStreamObject
。我目前正在处理的PDF是公司提供给我的,使用了Microsoft Word的导出到PDF功能创建。这生成了一个PyPDF2.generic.EncodedStreamObject
。经过探索,主要区别在于文本中的某些地方似乎存在字距调整。\n这给我在示例代码中带来了两个问题。首先,在main
函数中的if len(contents) > 0:
这一行似乎被错误地触发,并尝试使用EncodedStreamObject字典的键而不是EncodedStreamObject本身。为了解决这个问题,我注释掉了if块,并使用else块中的代码处理了这两种情况。\n第二个问题是,(我猜)字距标记破坏了我要替换的文本。我注意到并不是每一行都有字距调整,所以我假设字距标记并不是严格必要的,并尝试将其删除后的输出效果如何。文本的结构大致如下:[(Thi)4(s)-1(is t)2(ext)]。我将replace_text
函数中的replaced_line = line
这一行替换为replaced_line = \"[(\" + \"\".join(re.findall(r\'\\((.*?)\\)\', line)) + \")] TJ\"
。这样保留了观察到的结构,同时允许搜索替换文本。我验证了这实际上替换了该行的文本。\n然而,这些更改都没有阻止代码执行,但输出的PDF似乎完全没有变化,尽管使用print语句检查替换后的行是否有新文本,代码似乎工作正常。起初我认为这是因为process_data函数中决定是否为Encoded或Decoded的if块。然而,我查看了库的实际源代码位于此处,发现如果对象是Encoded,则它会生成一个Decoded版本,if块会反映这一点。我唯一的其他想法是我在main
函数中注释掉的if块并没有错误地捕获我的情况,而是以错误的方式处理它。我不知道如何修复它以正确处理它。\n我感觉离解决这个问题非常接近了,但我对接下来该做什么感到无助。我想在评论中问链接解决方案的发布者,但我没有足够的声望在SO上发表评论。有没有人对如何解决这个问题有任何线索?我并不特别关心使用哪个库或文件格式,但它必须保留我提供的Word文档的格式。我已经尝试过将其导出为HTML,但这会删除大部分格式和标题。我还尝试在Python中将.docx转换为PDF,但这要求我在机器上安装Word,这不是一个跨平台的解决方案。我还尝试了使用RTF,但据我所了解,该文件类型的解决方案是将其转换为.docx,然后再转换为PDF。\n以下是我目前拥有的全部代码:\n
import PyPDF2 import re 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 = "[(" + "".join(re.findall(r'\((.*?)\)', line)) + ")] TJ" 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(obj, replacements): data = obj.getData() decoded_data = data.decode('utf-8') replaced_data = replace_text(decoded_data, replacements) encoded_data = replaced_data.encode('utf-8') if obj.decodedSelf is not None: obj.decodedSelf.setData(encoded_data) else: obj.setData(encoded_data) pdf = PyPDF2.PdfFileReader("template.pdf") # pdf = PyPDF2.PdfFileReader("sample.pdf") writer = PyPDF2.PdfFileWriter() replacements = { "some text": "replacement text" } for page in pdf.pages: contents = page.getContents() # if len(contents) > 0: # for obj in contents: # streamObj = obj.getObject() # process_data(streamObj, replacements) # else: process_data(contents, replacements) writer.addPage(page) with open("output.pdf", 'wb') as out_file: writer.write(out_file)
\n编辑:\n我已经在一定程度上找到了问题的源头。这一行obj.decodedSelf.setData(encoded_data)
似乎没有正确地设置数据。在此行之后,我添加了以下代码:\n
print(encoded_data[:2000]) print("----------------------") print(obj.getData()[:2000])
\n第一条打印语句与第二条打印语句不同,这显然是不应该的。为了真正测试这是否属实,我将每一行都替换为[()],我知道有很多行已经是这样的。但是,不论如何,我都无法弄清楚为什么这个函数调用未能产生任何持久的更改。\n编辑2:\n我进一步确定了问题的所在。在EncodedStreamObject的getData方法中,它返回self.decodedSelf.getData()
,如果self.decodedSelf为True。然而,在执行obj.decodedSelf.setData(encoded_data)
之后,如果我执行print(bool(obj.decodedSelf))
,它将打印出False
。这意味着当EncodedStreamObject被访问以写入PDF时,它会重新解析旧的PDF并覆盖self.decodedSelf
对象!除非我进入并修复源代码,否则我不知道如何解决这个问题。\n编辑3:\n我设法让库使用具有替换内容的解码版本!在writer.addPage(page)
之前插入page[PyPDF2.pdf.NameObject(\"/Contents\")] = contents.decodedSelf
这一行,强制页面具有更新后的内容。不幸的是,我之前关于文本字距的假设是错误的。在替换文本后,我的一些文本神秘地从PDF中消失了。我猜这是因为格式不正确的原因。\n最终编辑:\n我觉得我应该把这个放在这里,以防其他人碰巧遇到这个问题。我从未真正成功地让它按预期工作。相反,我选择了一种模拟PDF的HTML/CSS解决方案。如果在HTML中添加以下样式标签,可以使其更像预期的PDF打印效果:\n
\n我建议对于任何想要做我所做的事情的人使用这个解决方案。有Python库可以将HTML转换为CSS,但它们不支持HTML5和CSS3(特别是它们不支持CSS的flex或grid)。您可以从任何浏览器将HTML页面打印为PDF以实现相同的效果。这明显不是问题的答案,所以我觉得最好将其作为编辑留下来。如果有人成功完成我所尝试的事情,请为我和其他人发布一个答案。