使用PyPDF4添加的部分PDF附件无法打开,如何排查与解决?
问题描述
我用PyPDF4给基础PDF添加附件,生成的最终PDF能正常打开,所有附件也都能显示,但部分附件(比如11.pdf)双击打不开,不过这个文件在本地文件夹里是能正常打开的。1.pdf到9.pdf作为附件都能正常打开。下面是我修改的用来添加而非替换附件的代码,求告知问题原因和解决办法:
#----addAttachment function of PyPDF4 is modified to add instead of replacing---# import os import PyPDF4 from PyPDF4 import PdfFileReader, PdfFileWriter from PyPDF4.generic import DecodedStreamObject, NameObject, DictionaryObject, createStringObject, ArrayObject def appendAttachment(myPdfFileWriterObj, fname, fdata): #The entry for file file_entry = DecodedStreamObject() file_entry.setData(fdata) file_entry.update({NameObject("/Type") : NameObject("/EmbeddedFile")}) #The Filespec entry efEntry = DictionaryObject() efEntry.update({NameObject("/F") : file_entry}) filespec = DictionaryObject() filespec.update({NameObject("/Type") : NameObject("/Filespec"), NameObject("/F") : createStringObject(fname), NameObject("/EF") : efEntry}) if "/Names" not in myPdfFileWriterObj._root_object.keys(): # No files attached yet. Create the entry for the root, as it needs a reference to the Filespec embeddedFilesNamesDictionary = DictionaryObject() embeddedFilesNamesDictionary.update({NameObject("/Names"): ArrayObject([createStringObject(fname), filespec])}) embeddedFilesDictionary = DictionaryObject() embeddedFilesDictionary.update({NameObject("/EmbeddedFiles"): embeddedFilesNamesDictionary}) myPdfFileWriterObj._root_object.update({NameObject("/Names"): embeddedFilesDictionary}) else: # There are files already attached. Append the new file. myPdfFileWriterObj._root_object["/Names"]["/EmbeddedFiles"]["/Names"].append(createStringObject(fname)) myPdfFileWriterObj._root_object["/Names"]["/EmbeddedFiles"]["/Names"].append(filespec) pdf_file = 'Contents.pdf' attachment_file = ['1.pdf','2.pdf','3.pdf','4.pdf','5.pdf','6.pdf','7.pdf', '8.pdf', '9.pdf', '11.pdf'] pdf_reader = PyPDF4.PdfFileReader(open(pdf_file,'rb')) pdf_writer = PyPDF4.PdfFileWriter() pdf_writer.addPage(pdf_reader.getPage(0)) for i in range(len(attachment_file)): with open(attachment_file[i],'rb') as f: appendAttachment(pdf_writer,attachment_file[i],f.read()) f.close() with open('CalBook.pdf','wb') as f: pdf_writer.write(f)
问题原因及解决办法
核心原因
- PDF对象未注册:PyPDF4的
PdfFileWriter需要将创建的所有PDF对象(如嵌入文件、文件规范字典)注册到内部引用表中,否则写入PDF时这些对象的引用会失效,导致附件无法被正确解析。你的代码中直接创建了DecodedStreamObject、DictionaryObject等,但没有调用_addObject()完成注册,部分文件(如11.pdf)可能因内容结构或大小触发了这个问题。 - 重复写入文件:循环内每次添加附件后都写入PDF,会导致writer对象状态未稳定就被持久化,可能引发对象引用的异常。
修复后的代码
#----修改PyPDF4的addAttachment函数为追加而非替换---# import PyPDF4 from PyPDF4 import PdfFileReader, PdfFileWriter from PyPDF4.generic import DecodedStreamObject, NameObject, DictionaryObject, createStringObject, ArrayObject def appendAttachment(myPdfFileWriterObj, fname, fdata): # 创建嵌入文件对象并注册到writer file_entry = DecodedStreamObject() file_entry.setData(fdata) file_entry.update({NameObject("/Type"): NameObject("/EmbeddedFile")}) myPdfFileWriterObj._addObject(file_entry) # 创建EF条目并注册 efEntry = DictionaryObject() efEntry.update({NameObject("/F"): file_entry}) myPdfFileWriterObj._addObject(efEntry) # 创建文件规范对象并注册 filespec = DictionaryObject() filespec.update({ NameObject("/Type"): NameObject("/Filespec"), NameObject("/F"): createStringObject(fname), NameObject("/EF"): efEntry }) myPdfFileWriterObj._addObject(filespec) if "/Names" not in myPdfFileWriterObj._root_object.keys(): # 首次添加附件,构建完整的名称字典结构并注册 embeddedFilesNamesDictionary = DictionaryObject() names_array = ArrayObject([createStringObject(fname), filespec]) embeddedFilesNamesDictionary.update({NameObject("/Names"): names_array}) myPdfFileWriterObj._addObject(embeddedFilesNamesDictionary) embeddedFilesDictionary = DictionaryObject() embeddedFilesDictionary.update({NameObject("/EmbeddedFiles"): embeddedFilesNamesDictionary}) myPdfFileWriterObj._addObject(embeddedFilesDictionary) myPdfFileWriterObj._root_object.update({NameObject("/Names"): embeddedFilesDictionary}) else: # 追加到现有附件列表 names_array = myPdfFileWriterObj._root_object["/Names"]["/EmbeddedFiles"]["/Names"] names_array.append(createStringObject(fname)) names_array.append(filespec) pdf_file = 'Contents.pdf' attachment_file = ['1.pdf','2.pdf','3.pdf','4.pdf','5.pdf','6.pdf','7.pdf', '8.pdf', '9.pdf', '11.pdf'] pdf_reader = PyPDF4.PdfFileReader(open(pdf_file,'rb')) pdf_writer = PyPDF4.PdfFileWriter() pdf_writer.addPage(pdf_reader.getPage(0)) # 批量添加所有附件 for fname in attachment_file: with open(fname,'rb') as f: appendAttachment(pdf_writer, fname, f.read()) # 所有附件添加完成后,统一写入最终PDF with open('CalBook.pdf','wb') as f: pdf_writer.write(f)
关键修复点
- 所有自定义创建的PDF对象都通过
myPdfFileWriterObj._addObject()完成注册,确保写入时生成有效的对象引用。 - 将PDF写入操作移到循环外部,避免重复写入导致的状态异常。
内容的提问来源于stack exchange,提问作者Raghu Varier
相关产品推荐
相关产品推荐

