You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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)
问题原因及解决办法

核心原因

  1. PDF对象未注册:PyPDF4的PdfFileWriter需要将创建的所有PDF对象(如嵌入文件、文件规范字典)注册到内部引用表中,否则写入PDF时这些对象的引用会失效,导致附件无法被正确解析。你的代码中直接创建了DecodedStreamObject、DictionaryObject等,但没有调用_addObject()完成注册,部分文件(如11.pdf)可能因内容结构或大小触发了这个问题。
  2. 重复写入文件:循环内每次添加附件后都写入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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 19:58:22