Skip to content

Commit b4a205a

Browse files
Merge pull request #1048 from chinapandaman/PPF-1047
PPF-1047: rename simple functions, move enable_adobe_mode to utils
2 parents 5d28d32 + 214513a commit b4a205a

File tree

4 files changed

+59
-61
lines changed

4 files changed

+59
-61
lines changed

PyPDFForm/filler.py

Lines changed: 12 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,23 @@
99
Adobe-specific settings for improved rendering in Adobe Reader.
1010
"""
1111

12-
from functools import lru_cache
1312
from io import BytesIO
1413
from typing import Dict, Union, cast
1514

1615
from pypdf import PdfReader, PdfWriter
17-
from pypdf.generic import BooleanObject, DictionaryObject, NameObject
16+
from pypdf.generic import DictionaryObject
1817

19-
from .constants import WIDGET_TYPES, AcroForm, Annots, NeedAppearances, Root
18+
from .constants import WIDGET_TYPES, Annots
2019
from .image import get_draw_image_resolutions, get_image_dimensions
2120
from .middleware.checkbox import Checkbox
2221
from .middleware.dropdown import Dropdown
2322
from .middleware.image import Image
2423
from .middleware.radio import Radio
2524
from .middleware.signature import Signature
2625
from .middleware.text import Text
27-
from .patterns import (simple_flatten_generic, simple_flatten_radio,
28-
simple_update_checkbox_value,
29-
simple_update_dropdown_value, simple_update_radio_value,
30-
simple_update_text_value)
26+
from .patterns import (flatten_generic, flatten_radio, update_checkbox_value,
27+
update_dropdown_value, update_radio_value,
28+
update_text_value)
3129
from .template import get_widget_key
3230
from .utils import stream_to_io
3331
from .watermark import create_watermarks_and_draw, merge_watermarks_with_pdf
@@ -101,42 +99,7 @@ def get_drawn_stream(to_draw: dict, stream: bytes, action: str) -> bytes:
10199
return merge_watermarks_with_pdf(stream, watermark_list)
102100

103101

104-
@lru_cache
105-
def enable_adobe_mode(pdf: bytes) -> bytes:
106-
"""Enables Adobe-specific settings in the PDF to ensure proper rendering of form fields.
107-
108-
This function modifies the PDF's AcroForm dictionary to include the `NeedAppearances` flag,
109-
which forces Adobe Reader to generate appearance streams for form fields. This ensures that
110-
the form fields are rendered correctly in Adobe Reader, especially when the form is filled
111-
programmatically.
112-
113-
Args:
114-
pdf (bytes): The PDF content as bytes.
115-
116-
Returns:
117-
bytes: The modified PDF content with Adobe mode enabled.
118-
"""
119-
reader = PdfReader(stream_to_io(pdf))
120-
writer = PdfWriter()
121-
122-
# https://stackoverflow.com/questions/47288578/pdf-form-filled-with-pypdf2-does-not-show-in-print
123-
if AcroForm in reader.trailer[Root]:
124-
if NeedAppearances in reader.trailer[Root][AcroForm]:
125-
return pdf
126-
else:
127-
reader.trailer[Root].update({NameObject(AcroForm): DictionaryObject()})
128-
reader.trailer[Root][AcroForm].update(
129-
{NameObject(NeedAppearances): BooleanObject(True)}
130-
)
131-
writer.append(reader)
132-
133-
with BytesIO() as f:
134-
writer.write(f)
135-
f.seek(0)
136-
return f.read()
137-
138-
139-
def simple_fill(
102+
def fill(
140103
template: bytes,
141104
widgets: Dict[str, WIDGET_TYPES],
142105
use_full_widget_name: bool,
@@ -185,9 +148,9 @@ def simple_fill(
185148
# flatten all
186149
if flatten:
187150
if isinstance(widget, Radio):
188-
simple_flatten_radio(annot)
151+
flatten_radio(annot)
189152
else:
190-
simple_flatten_generic(annot)
153+
flatten_generic(annot)
191154
if widget.value is None:
192155
continue
193156

@@ -196,17 +159,17 @@ def simple_fill(
196159
annot, widgets[key], images_to_draw[page_num + 1]
197160
)
198161
elif type(widget) is Checkbox:
199-
simple_update_checkbox_value(annot, widget.value)
162+
update_checkbox_value(annot, widget.value)
200163
elif isinstance(widget, Radio):
201164
if key not in radio_button_tracker:
202165
radio_button_tracker[key] = 0
203166
radio_button_tracker[key] += 1
204167
if widget.value == radio_button_tracker[key] - 1:
205-
simple_update_radio_value(annot)
168+
update_radio_value(annot)
206169
elif isinstance(widget, Dropdown):
207-
simple_update_dropdown_value(annot, widget)
170+
update_dropdown_value(annot, widget)
208171
elif isinstance(widget, Text):
209-
simple_update_text_value(annot, widget)
172+
update_text_value(annot, widget)
210173

211174
with BytesIO() as f:
212175
out.write(f)

PyPDFForm/patterns.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@
9696
]
9797

9898

99-
def simple_update_checkbox_value(annot: DictionaryObject, check: bool = False) -> None:
99+
def update_checkbox_value(annot: DictionaryObject, check: bool = False) -> None:
100100
"""
101101
Updates the value of a checkbox annotation, setting it to checked or unchecked.
102102
@@ -114,7 +114,7 @@ def simple_update_checkbox_value(annot: DictionaryObject, check: bool = False) -
114114
break
115115

116116

117-
def simple_update_radio_value(annot: DictionaryObject) -> None:
117+
def update_radio_value(annot: DictionaryObject) -> None:
118118
"""
119119
Updates the value of a radio button annotation, selecting it.
120120
@@ -134,7 +134,7 @@ def simple_update_radio_value(annot: DictionaryObject) -> None:
134134
break
135135

136136

137-
def simple_update_dropdown_value(annot: DictionaryObject, widget: Dropdown) -> None:
137+
def update_dropdown_value(annot: DictionaryObject, widget: Dropdown) -> None:
138138
"""
139139
Updates the value of a dropdown annotation, selecting an option from the list.
140140
@@ -157,7 +157,7 @@ def simple_update_dropdown_value(annot: DictionaryObject, widget: Dropdown) -> N
157157
annot[NameObject(I)] = ArrayObject([NumberObject(widget.value)])
158158

159159

160-
def simple_update_text_value(annot: DictionaryObject, widget: Text) -> None:
160+
def update_text_value(annot: DictionaryObject, widget: Text) -> None:
161161
"""
162162
Updates the value of a text annotation, setting the text content.
163163
@@ -176,7 +176,7 @@ def simple_update_text_value(annot: DictionaryObject, widget: Text) -> None:
176176
annot[NameObject(AP)] = TextStringObject(widget.value)
177177

178178

179-
def simple_flatten_radio(annot: DictionaryObject) -> None:
179+
def flatten_radio(annot: DictionaryObject) -> None:
180180
"""
181181
Flattens a radio button annotation by setting the ReadOnly flag, making it non-editable.
182182
@@ -192,7 +192,7 @@ def simple_flatten_radio(annot: DictionaryObject) -> None:
192192
)
193193

194194

195-
def simple_flatten_generic(annot: DictionaryObject) -> None:
195+
def flatten_generic(annot: DictionaryObject) -> None:
196196
"""
197197
Flattens a generic annotation by setting the ReadOnly flag, making it non-editable.
198198

PyPDFForm/utils.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
from typing import Any, BinaryIO, List, Union
2121

2222
from pypdf import PdfReader, PdfWriter
23-
from pypdf.generic import DictionaryObject
23+
from pypdf.generic import BooleanObject, DictionaryObject, NameObject
2424

25-
from .constants import UNIQUE_SUFFIX_LENGTH
25+
from .constants import UNIQUE_SUFFIX_LENGTH, AcroForm, NeedAppearances, Root
2626

2727

2828
@lru_cache
@@ -49,6 +49,41 @@ def stream_to_io(stream: bytes) -> BinaryIO:
4949
return result
5050

5151

52+
@lru_cache
53+
def enable_adobe_mode(pdf: bytes) -> bytes:
54+
"""Enables Adobe-specific settings in the PDF to ensure proper rendering of form fields.
55+
56+
This function modifies the PDF's AcroForm dictionary to include the `NeedAppearances` flag,
57+
which forces Adobe Reader to generate appearance streams for form fields. This ensures that
58+
the form fields are rendered correctly in Adobe Reader, especially when the form is filled
59+
programmatically.
60+
61+
Args:
62+
pdf (bytes): The PDF content as bytes.
63+
64+
Returns:
65+
bytes: The modified PDF content with Adobe mode enabled.
66+
"""
67+
reader = PdfReader(stream_to_io(pdf))
68+
writer = PdfWriter()
69+
70+
# https://stackoverflow.com/questions/47288578/pdf-form-filled-with-pypdf2-does-not-show-in-print
71+
if AcroForm in reader.trailer[Root]:
72+
if NeedAppearances in reader.trailer[Root][AcroForm]:
73+
return pdf
74+
else:
75+
reader.trailer[Root].update({NameObject(AcroForm): DictionaryObject()})
76+
reader.trailer[Root][AcroForm].update(
77+
{NameObject(NeedAppearances): BooleanObject(True)}
78+
)
79+
writer.append(reader)
80+
81+
with BytesIO() as f:
82+
writer.write(f)
83+
f.seek(0)
84+
return f.read()
85+
86+
5287
def remove_all_widgets(pdf: bytes) -> bytes:
5388
"""
5489
Removes all widgets (form fields) from a PDF, effectively flattening the form.

PyPDFForm/wrapper.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,16 @@
2525
from .constants import (DEFAULT_FONT, DEFAULT_FONT_COLOR, DEFAULT_FONT_SIZE,
2626
VERSION_IDENTIFIER_PREFIX, VERSION_IDENTIFIERS)
2727
from .coordinate import generate_coordinate_grid
28-
from .filler import enable_adobe_mode, simple_fill
28+
from .filler import fill
2929
from .font import (get_all_available_fonts, register_font,
3030
register_font_acroform)
3131
from .hooks import trigger_widget_hooks
3232
from .image import rotate_image
3333
from .middleware.signature import Signature
3434
from .middleware.text import Text
3535
from .template import build_widgets, update_widget_keys
36-
from .utils import (generate_unique_suffix, get_page_streams, merge_two_pdfs,
37-
remove_all_widgets)
36+
from .utils import (enable_adobe_mode, generate_unique_suffix,
37+
get_page_streams, merge_two_pdfs, remove_all_widgets)
3838
from .watermark import (copy_watermark_widgets, create_watermarks_and_draw,
3939
merge_watermarks_with_pdf)
4040
from .widgets.checkbox import CheckBoxWidget
@@ -387,7 +387,7 @@ def fill(
387387
if key in self.widgets:
388388
self.widgets[key].value = value
389389

390-
filled_stream, image_drawn_stream = simple_fill(
390+
filled_stream, image_drawn_stream = fill(
391391
self.read(),
392392
self.widgets,
393393
use_full_widget_name=getattr(self, "use_full_widget_name"),

0 commit comments

Comments
 (0)