【问题标题】:Python Django PDF Flattening of Form Fields表单字段的 Python Django PDF 展平
【发布时间】:2019-08-06 19:57:08
【问题描述】:

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

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

如何将我的 PDF-Form 转换为标准/平面 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

【问题讨论】:

    标签: python django pdf pypdf2 pdfrw


    【解决方案1】:

    解决方案非常简单,如有疑问请阅读文档(第 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  
    

    【讨论】:

    • 在一些非常无聊的文档中进行了出色的侦探工作。您知道这是否可以普遍应用于所有字段类型吗?我目前有复选框和文本字段,无法禁用复选框。
    • @markwalker_ 不幸的是,我无法让复选框以与文本字段相同的方式工作,我最终在这种情况下所做的是为“复选框”创建文本字段,然后我渲染了一个“X " 覆盖并压平它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-05
    • 2018-05-06
    • 2019-08-06
    • 2021-01-01
    • 2021-10-29
    • 2016-05-29
    • 1970-01-01
    相关资源
    最近更新 更多