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

使用PyPDF2嵌入JS到PDF时遇'_VirtualList'不可调用错误求助

问题

我尝试编写Python代码将JavaScript嵌入PDF文件,该JavaScript需实现以下功能:

  • 检查ID为"mac-address"的span是否存在。
  • 若不存在,创建一个ID为"mac-address"的隐藏span,并填入打开PDF的设备MAC地址。
  • 若存在,检查其值是否与打开PDF的设备MAC地址匹配。
  • 匹配则显示提示弹窗。
  • 不匹配则显示提示弹窗并删除PDF文件。

以下是我的代码:

import os
import subprocess
import PyPDF2
import uuid

# Get MAC address of device
mac_address = hex(uuid.getnode())

# Create JavaScript to be embedded in PDF
js_code = """
var mac_address_span = this.getField("mac-address");
if (mac_address_span == null) {
    var new_span = this.addField("mac-address", "text", 0, [0, 0, 0, 0]);
    new_span.value = "%s";
    new_span.display = display.hidden;
    app.alert("MAC address span created with value: " + new_span.value);
} else {
    if (mac_address_span.value == "%s") {
        app.alert("MAC address span exists with matching value: " + mac_address_span.value);
    } else {
        app.alert("MAC address span exists with non-matching value: " + mac_address_span.value);
        this.closeDoc(true);
    }
}
""" % (mac_address, mac_address)

# Open PDF
with open("example.pdf", mode="rb") as f:
    pdf_reader = PyPDF2.PdfReader(f)
    pdf_writer = PyPDF2.PdfWriter()

    # Embed JavaScript
    pdf_catalog = pdf_reader.trailer["/Root"]
    #pdf_catalog = pdf_reader.catalog
    pdf_catalog_js = pdf_catalog.get_object()
    pdf_catalog_js.update({
        PyPDF2.generic.NameObject('/Names'): PyPDF2.generic.DictionaryObject({
            PyPDF2.generic.NameObject('/JavaScript'): PyPDF2.generic.DictionaryObject({
                PyPDF2.generic.NameObject('/Names'): PyPDF2.generic.ArrayObject([
                    PyPDF2.generic.TextStringObject('mac-address'),
                    PyPDF2.generic.IndirectObject(len(pdf_writer._objects)+1, 0, pdf_writer)
                ])
            })
        })
    })

    # Add JavaScript action to document open event
    js_name = 'mac-address.js'
    js_action = pdf_writer.add_js(js_code)
    
    if js_action:
        pdf_writer._add_object(js_action)
    else:
        print("Failed to create JavaScript action object")

    pdf_open_action = PyPDF2.generic.DictionaryObject({
        PyPDF2.generic.NameObject('/S'): PyPDF2.generic.NameObject('/JavaScript'),
        PyPDF2.generic.NameObject('/JS'): PyPDF2.generic.NameObject(js_name)
    })


    pdf_catalog_js.update({
        PyPDF2.generic.NameObject('/OpenAction'): pdf_open_action
    })


    pdf_catalog_js.update({
        PyPDF2.generic.NameObject('/OpenAction'): pdf_open_action
    })

    # Add all pages to output PDF
    for page in range(pdf_reader.pages()):
        pdf_writer.addPage(pdf_reader.getPage(page))

    # Save output PDF
    with open("output.pdf", mode="wb") as output_file:
        pdf_writer.write(output_file)

# Open output PDF
if os.name == 'nt':  # for Windows OS
    os.startfile("output.pdf")
else:  # for Mac OS
    subprocess.call(["open", "output.pdf"])

运行时遇到以下错误:

for page in range(pdf_reader.pages()):
TypeError: '_VirtualList' object is not callable
解决方案

错误原因

在PyPDF2的新版本中,pdf_reader.pages是一个属性而非方法,不需要加括号调用。写成pdf_reader.pages()会把它当成方法执行,直接触发类型错误。此外还有两处API调用的版本兼容问题需要修正:

  1. 添加页面的方法在新版本中是add_page()而非旧版的addPage()
  2. 获取单页的方式改为直接通过pdf_reader.pages[索引]访问,而非getPage()方法

修正后的完整代码

import os
import subprocess
import PyPDF2
import uuid

# 获取设备MAC地址
mac_address = hex(uuid.getnode())

# 编写要嵌入PDF的JavaScript代码
js_code = """
var mac_address_span = this.getField("mac-address");
if (mac_address_span == null) {
    var new_span = this.addField("mac-address", "text", 0, [0, 0, 0, 0]);
    new_span.value = "%s";
    new_span.display = display.hidden;
    app.alert("MAC地址已写入隐藏字段: " + new_span.value);
} else {
    if (mac_address_span.value == "%s") {
        app.alert("MAC地址匹配,验证通过");
    } else {
        app.alert("MAC地址不匹配,文件将被关闭");
        this.closeDoc(true);
    }
}
""" % (mac_address, mac_address)

# 打开源PDF文件
with open("example.pdf", mode="rb") as f:
    pdf_reader = PyPDF2.PdfReader(f)
    pdf_writer = PyPDF2.PdfWriter()

    # 嵌入JavaScript到PDF目录
    pdf_catalog = pdf_reader.trailer["/Root"]
    pdf_catalog_js = pdf_catalog.get_object()
    pdf_catalog_js.update({
        PyPDF2.generic.NameObject('/Names'): PyPDF2.generic.DictionaryObject({
            PyPDF2.generic.NameObject('/JavaScript'): PyPDF2.generic.DictionaryObject({
                PyPDF2.generic.NameObject('/Names'): PyPDF2.generic.ArrayObject([
                    PyPDF2.generic.TextStringObject('mac-address'),
                    PyPDF2.generic.IndirectObject(len(pdf_writer._objects)+1, 0, pdf_writer)
                ])
            })
        })
    })

    # 添加JavaScript到文档打开事件
    js_name = 'mac-address.js'
    js_action = pdf_writer.add_js(js_code)
    
    if js_action:
        pdf_writer._add_object(js_action)
    else:
        print("创建JavaScript动作对象失败")

    pdf_open_action = PyPDF2.generic.DictionaryObject({
        PyPDF2.generic.NameObject('/S'): PyPDF2.generic.NameObject('/JavaScript'),
        PyPDF2.generic.NameObject('/JS'): PyPDF2.generic.NameObject(js_name)
    })

    pdf_catalog_js.update({
        PyPDF2.generic.NameObject('/OpenAction'): pdf_open_action
    })

    # 将所有页面添加到输出PDF
    for page in pdf_reader.pages:
        pdf_writer.add_page(page)

    # 保存输出PDF
    with open("output.pdf", mode="wb") as output_file:
        pdf_writer.write(output_file)

# 打开生成的PDF
if os.name == 'nt':  # Windows系统
    os.startfile("output.pdf")
else:  # macOS系统
    subprocess.call(["open", "output.pdf"])

内容的提问来源于stack exchange,提问作者the_nathan_x

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 23:45:00