表单字段的 Python Django PDF 扁平化

标签 python django pdf pypdf2 pdfrw

我有一个项目,我需要填写预制的 PDF,我想到的最合乎逻辑的解决方案是将预制的 PDF 制作成 PDF 表单,以便输入值应该放在标签中,然后我可以查看 PDF 中的表单标签,并将它们与值字典对齐。

我已经使用 完成了这项工作PyPDF2 .总的来说,我拍了一张网络表单的图像,然后打开 Acrobat 并根据图像中看到的字段创建了一个 PDF 表单,然后使用 PyPDF2 用于填写 PDF 表单字段,但需要注意的是,在某些浏览器中打印填写的值似乎有问题,Firefox 就是其中之一。

我如何将我的 PDF 表单转换为标准/平面 PDF 以便我可以保留预先填充的值,但会丢失可编辑的字段(因为我认为这是问题所在)?

from io import BytesIO

import PyPDF2
from django.http import HttpResponse

from PyPDF2.generic import BooleanObject, NameObject, IndirectObject


def pdf_view(request):
    template = 'templates/template.pdf'

    outfile = "templates/test.pdf"

    input_stream = open(template, "rb")
    pdf_reader = PyPDF2.PdfFileReader(input_stream, strict=False)
    if "/AcroForm" in pdf_reader.trailer["/Root"]:
        pdf_reader.trailer["/Root"]["/AcroForm"].update(
            {NameObject("/NeedAppearances"): BooleanObject(True)})

    pdf_writer = PyPDF2.PdfFileWriter()
    set_need_appearances_writer(pdf_writer)
    if "/AcroForm" in pdf_writer._root_object:
        # Acro form is form field, set needs appearances to fix printing issues
        pdf_writer._root_object["/AcroForm"].update(
            {NameObject("/NeedAppearances"): BooleanObject(True)})

    data_dict = {
        'first_name': 'John',
        'last_name': 'Smith',
        'email': 'mail@mail.com',
        'phone': '889-998-9967',
        'company': 'Amazing Inc.',
        'job_title': 'Dev',
        'street': '123 Main Way',
        'city': 'Johannesburg',
        'state': 'New Mexico',
        'zip': 96705,
        'country': 'USA',
        'topic': 'Who cares...'

    }

    pdf_writer.addPage(pdf_reader.getPage(0))
    pdf_writer.updatePageFormFieldValues(pdf_writer.getPage(0), data_dict)

    output_stream = BytesIO()
    pdf_writer.write(output_stream)

    # print(fill_in_pdf(template, data_dict).getvalue())

    # fill_in_pdf(template, data_dict).getvalue()
    response = HttpResponse(output_stream.getvalue(), content_type='application/pdf')
    response['Content-Disposition'] = 'inline; filename="completed.pdf"'
    input_stream.close()

    return response


def set_need_appearances_writer(writer):
    try:
        catalog = writer._root_object
        # get the AcroForm tree and add "/NeedAppearances attribute
        if "/AcroForm" not in catalog:
            writer._root_object.update({
                NameObject("/AcroForm"): IndirectObject(len(writer._objects), 0, writer)})

        need_appearances = NameObject("/NeedAppearances")
        writer._root_object["/AcroForm"][need_appearances] = BooleanObject(True)


    except Exception as e:
        print('set_need_appearances_writer() catch : ', repr(e))

    return writer

最佳答案

解决方案非常简单,如有疑问,请阅读文档( 第 552/978 页 ):

https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf

我需要做的就是将字段标志的位位置更改为 1,使字段只读,如下所示:

from io import BytesIO

import PyPDF2
from django.http import HttpResponse

from PyPDF2.generic import BooleanObject, NameObject, IndirectObject, NumberObject


def pdf(request):
    template = 'templates/template.pdf'

    outfile = "templates/test.pdf"

    input_stream = open(template, "rb")
    pdf_reader = PyPDF2.PdfFileReader(input_stream, strict=False)
    if "/AcroForm" in pdf_reader.trailer["/Root"]:
        pdf_reader.trailer["/Root"]["/AcroForm"].update(
            {NameObject("/NeedAppearances"): BooleanObject(True)})

    pdf_writer = PyPDF2.PdfFileWriter()
    set_need_appearances_writer(pdf_writer)
    if "/AcroForm" in pdf_writer._root_object:
        # Acro form is form field, set needs appearances to fix printing issues
        pdf_writer._root_object["/AcroForm"].update(
            {NameObject("/NeedAppearances"): BooleanObject(True)})

    data_dict = {
        'first_name': 'John\n',
        'last_name': 'Smith\n',
        'email': 'mail@mail.com\n',
        'phone': '889-998-9967\n',
        'company': 'Amazing Inc.\n',
        'job_title': 'Dev\n',
        'street': '123 Main Way\n',
        'city': 'Johannesburg\n',
        'state': 'New Mexico\n',
        'zip': 96705,
        'country': 'USA\n',
        'topic': 'Who cares...\n'

    }

    pdf_writer.addPage(pdf_reader.getPage(0))
    page = pdf_writer.getPage(0)
    pdf_writer.updatePageFormFieldValues(page, data_dict)
    for j in range(0, len(page['/Annots'])):
        writer_annot = page['/Annots'][j].getObject()
        for field in data_dict:
            # -----------------------------------------------------BOOYAH!
            if writer_annot.get('/T') == field:
                writer_annot.update({
                    NameObject("/Ff"): NumberObject(1)
                })
             # -----------------------------------------------------
    output_stream = BytesIO()
    pdf_writer.write(output_stream)

    response = HttpResponse(output_stream.getvalue(), content_type='application/pdf')
    response['Content-Disposition'] = 'inline; filename="completed.pdf"'
    input_stream.close()

    return response


def set_need_appearances_writer(writer):
    try:
        catalog = writer._root_object
        # get the AcroForm tree and add "/NeedAppearances attribute
        if "/AcroForm" not in catalog:
            writer._root_object.update({
                NameObject("/AcroForm"): IndirectObject(len(writer._objects), 0, writer)})

        need_appearances = NameObject("/NeedAppearances")
        writer._root_object["/AcroForm"][need_appearances] = BooleanObject(True)


    except Exception as e:
        print('set_need_appearances_writer() catch : ', repr(e))

    return writer  

关于表单字段的 Python Django PDF 扁平化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55187651/

相关文章:

java - 有没有办法让这个 "send to printer"Java 方法起作用?

java - 使用 iText 计算 PDF 上的附件数量

java - 将 JSP 页面下载为 PDF

python - Keras 评估给出的结果与历史不同。哪一个是准确的

python - 为什么 SQLite 中的多列索引会降低查询的性能,除非索引所有列?

python - 如何绘制经验 cdf (ecdf)

python - 加速运行从数据库中获取的一些数据的大型进程

django - 在 Django 中对多表继承进行注释

django - 如何实现投票验证系统?

python - Python 类实例是否有一个属性来获取类名?