pypdf
pypdf copied to clipboard
Can't Fill PDFs without /DR dictionary
I am trying to fill out a PDF form, but running into an issue where the /Font dictionary is not populated, so nothing can be written to the PDF.
Environment
Which environment were you using when you encountered the problem?
$ python -m platform
macOS-14.4.1-arm64-arm-64bit
$ python -c "import pypdf;print(pypdf._debug_versions)"
pypdf==4.2.0, crypt_provider=('pycryptodome', '3.20.0'), PIL=none
Code + PDF
This is a minimal, complete example that shows the issue:
from pypdf import PdfReader, PdfWriter
from pypdf.constants import AnnotationDictionaryAttributes
reader = PdfReader("modified_example.pdf")
writer = PdfWriter()
writer.append(reader)
fields = []
for page in reader.pages:
writer.reattach_fields(page)
for annot in page.annotations:
annot = annot.get_object()
if annot[AnnotationDictionaryAttributes.Subtype] == "/Widget":
fields.append(annot)
if annot['/FT'] == "/Tx":
fieldName = annot["/T"]
writer.update_page_form_field_values(
writer.pages[page.page_number],
{fieldName: "Brooks"},
auto_regenerate=False,
)
with open("test2.pdf", "wb") as output_stream:
writer.write(output_stream)
Share here the PDF file(s) that cause the issue. The smaller they are, the better. Let us know if we may add them to our tests!
Traceback
This is the complete traceback I see:
Traceback (most recent call last):
File "/Users/brooks.watson/PdfGenerator/PyPdfTest.py", line 16, in <module>
writer.update_page_form_field_values(
File "/Users/brooks.watson/PdfGenerator/venv/lib/python3.9/site-packages/pypdf/_writer.py", line 977, in update_page_form_field_values
self._update_field_annotation(writer_parent_annot, writer_annot)
File "/Users/brooks.watson/PdfGenerator/venv/lib/python3.9/site-packages/pypdf/_writer.py", line 800, in _update_field_annotation
dr = dr.get_object().get("/Font", DictionaryObject()).get_object()
AttributeError: 'dict' object has no attribute 'get_object'
Process finished with exit code 1
For reference, adding the following code to the _writer.py file fixes the issue:
# Retrieve font information from local DR ...
# Original code
dr: Any = cast(
DictionaryObject,
cast(
DictionaryObject,
anno.get_inherited(
"/DR",
cast(
DictionaryObject, self.root_object[CatalogDictionary.ACRO_FORM]
).get("/DR", DictionaryObject()),
),
).get_object(),
)
# New Code
if "/Font" not in dr or not isinstance(dr["/Font"], DictionaryObject):
dr[NameObject("/Font")] = DictionaryObject()
font_dict = dr["/Font"]
# Check if the specific font (e.g., Helvetica) is in /Font
font_name = NameObject("/Helvetica")
if font_name not in font_dict:
font_entry = DictionaryObject({
NameObject("/Type"): NameObject("/Font"),
NameObject("/Subtype"): NameObject("/Type1"),
NameObject("/BaseFont"): NameObject("/Helvetica"),
NameObject("/Encoding"): NameObject("/WinAnsiEncoding")
})
font_dict[font_name] = font_entry
#Original code
dr = dr.get("/Font", DictionaryObject()).get_object()