pythonubuntupdfprintingcups

Ubuntu printing PDF with fields does not print filled in fields when filled with Python fillpdf


I'm currently using the fillpdf Python library to fill a template PDF with generated data. I'm running this process on Ubuntu 20.04 server, trying to print to a Kyocera ECOSYS p4060dn.

An example of how I'm using the code:

import fillpdf
from fillpdf import fillpdfs

data_dict = {
        'Season1': '2025-2026',
        'Season2': '2025-26',
        'MemberName': 'Test',
        'Address': '123 Sesame St',
        'CityStateZip': 'Birmingham, AK 12345-6789',
        'Number': '9999999',
        'Level': '1'
    }

fillpdfs.write_fillable_pdf('/home/myuser/template.pdf', '/home/myuser/filled.pdf', data_dict, flatten=True)

This appears to work as intended, as when I copy the filled PDf to my Windows machine and open it, the fields are filled with the provided data.

However my issue is that when I try to print the PDF, it prints without the forms filled in. There are no cups errors in /var/log/cups/error_log related to the print, and access_log similarly looks all good.

I'm printing by using the command

lp -d [printer] /path/to/PDf

I have the printer setup with the right driver, and strangely if I fill the PDF on my windows machine and copy it up to the Ubuntu server then print, it prints just fine, fields filled and all.

I've tried using different libraries like PyPDF2, PyPDF, and pdfrw to fill the fields and flatten, all to the same result of the fields being filled but not printing with them filled, or occasionally no print at all.

Any guidance would be greatly appreciated

EDIT: The fields are set to print in Acrobat.


Solution

  • Thanks to KJ for pointing me in the right direction!

    A coworker wrote up a different way to fill the fields using pdfrw, example below:

    from pdfrw import PdfReader, PdfWriter, PdfDict, PdfObject, PdfName, PageMerge
    from pdfrw.objects.pdfstring import PdfString
    
    def fill_pdf_fields(input_path, output_path):
        pdf = PdfReader(input_path)
    
        # Ensure viewer regenerates appearances
        if not pdf.Root.AcroForm:
            pdf.Root.AcroForm = PdfDict(NeedAppearances=PdfObject('true'))
        else:
            pdf.Root.AcroForm.update(PdfDict(NeedAppearances=PdfObject('true')))
    
        for page in pdf.pages:
            annotations = page.Annots
            if annotations:
                for annot in annotations:
                    if annot.Subtype == PdfName('Widget') and annot.T:
                        field_name = str(annot.T)[1:-1]
    
    
                        if field_name == "MemberName": annot.V = PdfObject(f'(Test)')
                        if field_name == "Address": annot.V = PdfObject(f'(123 Sesame St)')
                        if field_name == "CityStateZip": annot.V = PdfObject(f'(Birmingham, AK 12345-6789)')
                        if field_name == "Level": annot.V = PdfObject(f'(1)')
                        if field_name == "OfficialsNumber": annot.V = PdfObject(f'(9999999)')
                        if field_name == "Season2": annot.V = PdfObject(f'(2025-26)')
                        if field_name == "Season1": annot.V = PdfObject(f'(2025-2026)')
    
    
        PdfWriter().write(output_path, pdf)
        print(f"Filled PDF saved to: {output_path}")
    
    
    def flatten_pdf_fields(input_path, output_path):
        template_pdf = PdfReader(input_path)
    
        for page in template_pdf.pages:
            annotations = page.Annots
            if annotations:
                for annot in annotations:
                    if annot.Subtype == PdfName('Widget') and annot.T and annot.V:
                        # Remove interactive field appearance
                        annot.update({
                            PdfName('F'): PdfObject('4'),  # Make field read-only
                            PdfName('AP'): None  # Remove appearance stream
                        })
    
            # Flatten page by merging its own content (no overlay)
            PageMerge(page).render()
    
        PdfWriter(output_path, trailer=template_pdf).write()
        print(f"Flattened PDF saved to: {output_path}")
    
    
    if __name__ == "__main__":
        fill_pdf_fields(template_pdf, filled_pdf)
        flatten_pdf_fields(filled_pdf, flattened_pdf)
    

    I researched interactions with NeedAppearances, and found this Stack post:
    NeedAppearances=pdfrw.PdfObject('true') forces manual pdf save in Acrobat Reader

    The answer provides a code snippet that from what I can tell, acts as a reader generating those appearance streams so the filled in fields actually show their contents.

    Code snippet for reference:

    from pikepdf import Pdf
    
    with Pdf.open('source_pdf.pdf') as pdf:
        pdf.generate_appearance_streams()
        pdf.save('output.pdf')