PyPDFForm 2.4.0__py3-none-any.whl → 3.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of PyPDFForm might be problematic. Click here for more details.

@@ -1,86 +1,71 @@
1
1
  # -*- coding: utf-8 -*-
2
- """Provides middleware for PDF signature field widgets.
2
+ """
3
+ Module representing a signature widget.
3
4
 
4
- This module contains the Signature class which handles:
5
- - Signature image processing
6
- - Aspect ratio preservation
7
- - File path resolution
8
- - PDF form field integration
5
+ This module defines the Signature class, which is a subclass of the
6
+ Widget class. It represents a signature form field in a PDF document,
7
+ allowing users to add their signature as an image.
9
8
  """
10
9
 
11
10
  from os.path import expanduser
12
- from typing import BinaryIO, Union
11
+ from typing import Union
13
12
 
14
13
  from ..adapter import fp_or_f_obj_or_stream_to_stream
15
14
  from .base import Widget
16
15
 
17
16
 
18
17
  class Signature(Widget):
19
- """Middleware for PDF signature field widgets.
20
-
21
- Handles all aspects of signature field processing including:
22
- - Signature image handling
23
- - Aspect ratio control
24
- - File path resolution
25
- - PDF form field integration
18
+ """
19
+ Represents a signature widget.
26
20
 
27
- Inherits from Widget base class and extends it with signature-specific features.
21
+ The Signature class provides a concrete implementation for
22
+ signature form fields. It inherits from the Widget class and
23
+ implements the schema_definition, sample_value, and stream
24
+ properties.
28
25
  """
29
26
 
30
27
  preserve_aspect_ratio = True
31
- """Whether to preserve the original image's aspect ratio when scaling."""
32
-
33
- def __init__(
34
- self,
35
- name: str,
36
- value: Union[bytes, str, BinaryIO] = None,
37
- ) -> None:
38
- """Initializes a new signature field widget.
39
-
40
- Args:
41
- name: Field name/key for the signature
42
- value: Signature image as bytes, file path, or file object (default: None)
43
- """
44
-
45
- super().__init__(name, value)
46
28
 
47
29
  @property
48
30
  def schema_definition(self) -> dict:
49
- """Generates a JSON schema definition for the signature field.
31
+ """
32
+ Returns the schema definition for the signature.
33
+
34
+ The schema definition is a dictionary that describes the
35
+ data type and other constraints for the signature value,
36
+ which is expected to be a string representing the path to
37
+ the signature image.
50
38
 
51
39
  Returns:
52
- dict: Schema properties including:
53
- - type: string (file path)
54
- - description (if available from base class)
40
+ dict: A dictionary representing the schema definition.
55
41
  """
56
-
57
- return {"type": "string"}
42
+ return {"type": "string", **super().schema_definition}
58
43
 
59
44
  @property
60
45
  def sample_value(self) -> str:
61
- """Generates a sample value for the signature field.
46
+ """
47
+ Returns a sample value for the signature.
62
48
 
63
- Returns a default sample image path from the user's Downloads folder.
49
+ The sample value is used to generate example data for the
50
+ signature field. It returns the path to a sample image file.
64
51
 
65
52
  Returns:
66
- str: Path to sample image file
53
+ str: A sample value for the signature.
67
54
  """
68
-
69
55
  return expanduser("~/Downloads/sample_image.jpg")
70
56
 
71
57
  @property
72
58
  def stream(self) -> Union[bytes, None]:
73
- """Converts the signature image to a byte stream.
59
+ """
60
+ Returns the stream of the signature image.
74
61
 
75
- Handles conversion of:
76
- - Raw image bytes
77
- - File paths
78
- - File-like objects
62
+ This method reads the signature image from the file path
63
+ specified in the value attribute and returns the image data
64
+ as a stream of bytes.
79
65
 
80
66
  Returns:
81
- Union[bytes, None]: Image data as bytes, or None if no value set
67
+ Union[bytes, None]: The stream of the signature image.
82
68
  """
83
-
84
69
  return (
85
70
  fp_or_f_obj_or_stream_to_stream(self.value)
86
71
  if self.value is not None
@@ -1,67 +1,78 @@
1
1
  # -*- coding: utf-8 -*-
2
- """Provides middleware for PDF text field widgets.
3
-
4
- This module contains the Text class which handles:
5
- - Text field value management
6
- - Font properties (family, size, color)
7
- - Text wrapping and formatting
8
- - Comb field (fixed character spacing) support
9
- - Preview mode for form field visualization
10
2
  """
3
+ Module representing a text field widget.
11
4
 
12
- from typing import Any
5
+ This module defines the Text class, which is a subclass of the
6
+ Widget class. It represents a text field form field in a PDF document,
7
+ allowing users to enter text.
8
+ """
13
9
 
14
10
  from .base import Widget
15
11
 
16
12
 
17
13
  class Text(Widget):
18
- """Middleware for PDF text field widgets.
19
-
20
- Handles all aspects of text field processing including:
21
- - Value conversion and validation
22
- - Font styling and formatting
23
- - Multiline text wrapping
24
- - Comb field character spacing
25
- - Preview mode rendering
26
-
27
- Inherits from Widget base class and extends it with text-specific features.
28
14
  """
15
+ Represents a text field widget.
16
+
17
+ The Text class provides a concrete implementation for text field
18
+ form fields. It inherits from the Widget class and implements
19
+ the value, schema_definition, and sample_value properties. It
20
+ also defines a number of attributes that can be used to customize
21
+ the appearance and behavior of the text field, such as font,
22
+ font_size, font_color, comb, alignment, and multiline.
23
+ """
24
+
25
+ SET_ATTR_TRIGGER_HOOK_MAP = {
26
+ "font": "update_text_field_font",
27
+ "font_size": "update_text_field_font_size",
28
+ "font_color": "update_text_field_font_color",
29
+ "comb": "update_text_field_comb",
30
+ "alignment": "update_text_field_alignment",
31
+ "multiline": "update_text_field_multiline",
32
+ }
29
33
 
30
34
  def __init__(
31
35
  self,
32
36
  name: str,
33
37
  value: str = None,
34
38
  ) -> None:
35
- """Initializes a new text field widget.
39
+ """
40
+ Initializes a text field widget.
36
41
 
37
42
  Args:
38
- name: Field name/key for the text field
39
- value: Initial text value (default: None)
43
+ name (str): The name of the text field.
44
+ value (str): The initial value of the text field. Defaults to None.
45
+
46
+ Attributes:
47
+ font (str): The font of the text field. Defaults to None.
48
+ font_size (int): The font size of the text field. Defaults to None.
49
+ font_color (str): The font color of the text field. Defaults to None.
50
+ comb (bool): Whether the text field is a comb field. Defaults to None.
51
+ alignment (str): The alignment of the text field. Defaults to None.
52
+ multiline (bool): Whether the text field is multiline. Defaults to None.
53
+ max_length (int): The maximum length of the text field. Defaults to None.
40
54
  """
41
-
42
55
  super().__init__(name, value)
43
56
 
44
57
  self.font = None
45
58
  self.font_size = None
46
59
  self.font_color = None
47
- self.text_wrap_length = None
48
- self.max_length = None
49
60
  self.comb = None
50
- self.character_paddings = []
51
- self.text_lines = None
52
- self.text_line_x_coordinates = None
53
- self.preview = False
61
+ self.alignment = None
62
+ self.multiline = None
63
+
64
+ self.max_length = None
54
65
 
55
66
  @property
56
- def value(self) -> Any:
57
- """Gets the text field's current value with type conversion.
67
+ def value(self) -> str:
68
+ """
69
+ Returns the value of the text field.
58
70
 
59
- Converts numeric values to strings automatically.
71
+ If the value is an integer or float, it is converted to a string.
60
72
 
61
73
  Returns:
62
- Any: The text value as a string (converted if numeric)
74
+ str: The value of the text field.
63
75
  """
64
-
65
76
  if isinstance(self._value, (int, float)):
66
77
  return str(self._value)
67
78
 
@@ -69,27 +80,25 @@ class Text(Widget):
69
80
 
70
81
  @value.setter
71
82
  def value(self, value: str) -> None:
72
- """Sets the text field's value.
83
+ """
84
+ Sets the value of the text field.
73
85
 
74
86
  Args:
75
- value: New text value (will be converted to string if numeric)
87
+ value (str): The value to set.
76
88
  """
77
-
78
89
  self._value = value
79
90
 
80
91
  @property
81
92
  def schema_definition(self) -> dict:
82
- """Generates a JSON schema definition for the text field.
93
+ """
94
+ Returns the schema definition for the text field.
83
95
 
84
- Includes:
85
- - Type constraint (string)
86
- - Max length if specified
87
- - Any inherited schema properties
96
+ The schema definition is a dictionary that describes the
97
+ data type and other constraints for the text field value.
88
98
 
89
99
  Returns:
90
- dict: Complete JSON schema definition
100
+ dict: A dictionary representing the schema definition.
91
101
  """
92
-
93
102
  result = {"type": "string"}
94
103
 
95
104
  if self.max_length is not None:
@@ -99,14 +108,16 @@ class Text(Widget):
99
108
 
100
109
  @property
101
110
  def sample_value(self) -> str:
102
- """Generates a sample value demonstrating the text field's capacity.
111
+ """
112
+ Returns a sample value for the text field.
103
113
 
104
- Uses the field name as the sample value, truncated to max_length if specified.
114
+ The sample value is used to generate example data for the
115
+ text field. It returns the name of the field, truncated to
116
+ the maximum length if specified.
105
117
 
106
118
  Returns:
107
- str: Sample text value for demonstration purposes
119
+ str: A sample value for the text field.
108
120
  """
109
-
110
121
  return (
111
122
  self.name[: self.max_length] if self.max_length is not None else self.name
112
123
  )
PyPDFForm/patterns.py CHANGED
@@ -1,27 +1,19 @@
1
1
  # -*- coding: utf-8 -*-
2
- """Pattern matching utilities for PDF form widgets.
3
-
4
- This module provides:
5
- - Pattern definitions for identifying widget types and properties
6
- - Functions for updating widget states and appearances
7
- - Support for common PDF form operations like flattening fields
8
-
9
- Patterns are used throughout PyPDFForm to:
10
- - Classify widget types (text, checkbox, radio, etc.)
11
- - Extract widget properties (alignment, colors, flags)
12
- - Modify widget states (values, appearances, flags)
2
+ """
3
+ This module defines patterns and utility functions for interacting with PDF form fields.
13
4
 
14
- The module also contains utility functions for common PDF form operations
15
- like updating field values and flattening form fields.
5
+ It includes patterns for identifying different types of widgets (e.g., text fields,
6
+ checkboxes, radio buttons, dropdowns, images, and signatures) based on their
7
+ properties in the PDF's annotation dictionary. It also provides utility functions
8
+ for updating and flattening these widgets.
16
9
  """
17
10
 
18
11
  from pypdf.generic import (ArrayObject, DictionaryObject, NameObject,
19
12
  NumberObject, TextStringObject)
20
13
 
21
- from .constants import (AP, AS, BC, BG, BS, CA, DA, DV, FT,
22
- IMAGE_FIELD_IDENTIFIER, JS, MK, MULTILINE, READ_ONLY,
23
- TU, A, Btn, Ch, D, Ff, I, N, Off, Opt, Parent, Q, S,
24
- Sig, T, Tx, V, W, Yes)
14
+ from .constants import (AP, AS, DV, FT, IMAGE_FIELD_IDENTIFIER, JS, READ_ONLY,
15
+ TU, A, Btn, Ch, Ff, I, N, Off, Opt, Parent, Sig, T, Tx,
16
+ V, Yes)
25
17
  from .middleware.checkbox import Checkbox
26
18
  from .middleware.dropdown import Dropdown
27
19
  from .middleware.image import Image
@@ -103,52 +95,18 @@ DROPDOWN_CHOICE_PATTERNS = [
103
95
  {Parent: {Opt: True}},
104
96
  ]
105
97
 
106
- WIDGET_ALIGNMENT_PATTERNS = [
107
- {Q: True},
108
- {Parent: {Q: True}},
109
- ]
110
-
111
- TEXT_FIELD_FLAG_PATTERNS = [
112
- {Ff: True},
113
- {Parent: {Ff: True}},
114
- ]
115
-
116
- TEXT_FIELD_APPEARANCE_PATTERNS = [
117
- {DA: True},
118
- {Parent: {DA: True}},
119
- ]
120
-
121
- BUTTON_STYLE_PATTERNS = [
122
- {MK: {CA: True}},
123
- ]
124
-
125
- BORDER_COLOR_PATTERNS = [
126
- {MK: {BC: True}},
127
- ]
128
-
129
- BACKGROUND_COLOR_PATTERNS = [
130
- {MK: {BG: True}},
131
- ]
132
-
133
- BORDER_WIDTH_PATTERNS = [{BS: {W: True}}]
134
-
135
- BORDER_STYLE_PATTERNS = [{BS: {S: True}}]
136
-
137
- BORDER_DASH_ARRAY_PATTERNS = [{BS: {D: True}}]
138
-
139
98
 
140
- def simple_update_checkbox_value(annot: DictionaryObject, check: bool = False) -> None:
141
- """Update checkbox annotation values based on check state.
99
+ def update_checkbox_value(annot: DictionaryObject, check: bool = False) -> None:
100
+ """
101
+ Updates the value of a checkbox annotation, setting it to checked or unchecked.
142
102
 
143
- Modifies the appearance state (AS) and value (V) of a checkbox annotation
144
- to reflect the desired checked/unchecked state. Uses the annotation's
145
- appearance dictionary (AP/N) to determine valid states.
103
+ This function modifies the appearance state (AS) and value (V) of the checkbox
104
+ annotation to reflect the desired state (checked or unchecked).
146
105
 
147
106
  Args:
148
- annot: PDF annotation dictionary to modify
149
- check: Whether the checkbox should be checked (True) or unchecked (False)
107
+ annot (DictionaryObject): The checkbox annotation dictionary.
108
+ check (bool): True to check the checkbox, False to uncheck it. Defaults to False.
150
109
  """
151
-
152
110
  for each in annot[AP][N]:
153
111
  if (check and str(each) != Off) or (not check and str(each) == Off):
154
112
  annot[NameObject(AS)] = NameObject(each)
@@ -156,18 +114,16 @@ def simple_update_checkbox_value(annot: DictionaryObject, check: bool = False) -
156
114
  break
157
115
 
158
116
 
159
- def simple_update_radio_value(annot: DictionaryObject) -> None:
160
- """Update radio button annotation values to selected state.
117
+ def update_radio_value(annot: DictionaryObject) -> None:
118
+ """
119
+ Updates the value of a radio button annotation, selecting it.
161
120
 
162
- Modifies the appearance state (AS) of a radio button annotation and updates
163
- the parent's value (V) to reflect the selected state. Removes 'Opt' entry
164
- from parent dictionary if present. Uses the annotation's appearance
165
- dictionary (AP/N) to determine valid states.
121
+ This function modifies the appearance state (AS) and value (V) of the radio button's
122
+ parent dictionary to reflect the selected state.
166
123
 
167
124
  Args:
168
- annot: PDF radio button annotation dictionary to modify
125
+ annot (DictionaryObject): The radio button annotation dictionary.
169
126
  """
170
-
171
127
  if Opt in annot[Parent]:
172
128
  del annot[Parent][Opt]
173
129
 
@@ -178,41 +134,41 @@ def simple_update_radio_value(annot: DictionaryObject) -> None:
178
134
  break
179
135
 
180
136
 
181
- def simple_update_dropdown_value(annot: DictionaryObject, widget: Dropdown) -> None:
182
- """Update dropdown annotation values based on widget selection.
137
+ def update_dropdown_value(annot: DictionaryObject, widget: Dropdown) -> None:
138
+ """
139
+ Updates the value of a dropdown annotation, selecting an option from the list.
183
140
 
184
- Modifies the value (V), appearance (AP), and index (I) of a dropdown
185
- annotation to reflect the currently selected choice from the widget.
186
- Handles both standalone dropdowns and those with parent annotations.
141
+ This function modifies the value (V) and appearance (AP) of the dropdown
142
+ annotation to reflect the selected option. It also updates the index (I)
143
+ of the selected option.
187
144
 
188
145
  Args:
189
- annot: PDF dropdown annotation dictionary to modify
190
- widget: Dropdown widget containing the selected value
146
+ annot (DictionaryObject): The dropdown annotation dictionary.
147
+ widget (Dropdown): The Dropdown widget object containing the selected value.
191
148
  """
192
-
149
+ choices = widget.choices or []
193
150
  if Parent in annot and T not in annot:
194
151
  annot[NameObject(Parent)][NameObject(V)] = TextStringObject(
195
- widget.choices[widget.value]
152
+ choices[widget.value]
196
153
  )
197
- annot[NameObject(AP)] = TextStringObject(widget.choices[widget.value])
154
+ annot[NameObject(AP)] = TextStringObject(choices[widget.value])
198
155
  else:
199
- annot[NameObject(V)] = TextStringObject(widget.choices[widget.value])
200
- annot[NameObject(AP)] = TextStringObject(widget.choices[widget.value])
156
+ annot[NameObject(V)] = TextStringObject(choices[widget.value])
157
+ annot[NameObject(AP)] = TextStringObject(choices[widget.value])
201
158
  annot[NameObject(I)] = ArrayObject([NumberObject(widget.value)])
202
159
 
203
160
 
204
- def simple_update_text_value(annot: DictionaryObject, widget: Text) -> None:
205
- """Update text field annotation values based on widget content.
161
+ def update_text_value(annot: DictionaryObject, widget: Text) -> None:
162
+ """
163
+ Updates the value of a text annotation, setting the text content.
206
164
 
207
- Modifies the value (V) and appearance (AP) of a text field annotation to
208
- reflect the current value from the text widget. Handles both standalone
209
- text fields and those with parent annotations.
165
+ This function modifies the value (V) and appearance (AP) of the text
166
+ annotation to reflect the new text content.
210
167
 
211
168
  Args:
212
- annot: PDF text field annotation dictionary to modify
213
- widget: Text widget containing the value to set
169
+ annot (DictionaryObject): The text annotation dictionary.
170
+ widget (Text): The Text widget object containing the text value.
214
171
  """
215
-
216
172
  if Parent in annot and T not in annot:
217
173
  annot[NameObject(Parent)][NameObject(V)] = TextStringObject(widget.value)
218
174
  annot[NameObject(AP)] = TextStringObject(widget.value)
@@ -221,33 +177,32 @@ def simple_update_text_value(annot: DictionaryObject, widget: Text) -> None:
221
177
  annot[NameObject(AP)] = TextStringObject(widget.value)
222
178
 
223
179
 
224
- def simple_flatten_radio(annot: DictionaryObject) -> None:
225
- """Flatten radio button annotation by making it read-only.
180
+ def flatten_radio(annot: DictionaryObject) -> None:
181
+ """
182
+ Flattens a radio button annotation by setting the ReadOnly flag, making it non-editable.
226
183
 
227
- Modifies the field flags (Ff) of a radio button's parent annotation
228
- to set the read-only flag, effectively flattening the field and
229
- preventing further user interaction.
184
+ This function modifies the Ff (flags) entry in the radio button's parent
185
+ dictionary to set the ReadOnly flag, preventing the user from changing the
186
+ selected option.
230
187
 
231
188
  Args:
232
- annot: PDF radio button annotation dictionary to flatten
189
+ annot (DictionaryObject): The radio button annotation dictionary.
233
190
  """
234
-
235
191
  annot[NameObject(Parent)][NameObject(Ff)] = NumberObject(
236
192
  int(annot[NameObject(Parent)].get(NameObject(Ff), 0)) | READ_ONLY
237
193
  )
238
194
 
239
195
 
240
- def simple_flatten_generic(annot: DictionaryObject) -> None:
241
- """Flatten generic annotation by making it read-only.
196
+ def flatten_generic(annot: DictionaryObject) -> None:
197
+ """
198
+ Flattens a generic annotation by setting the ReadOnly flag, making it non-editable.
242
199
 
243
- Modifies the field flags (Ff) of an annotation to set the read-only flag,
244
- effectively flattening the field and preventing further user interaction.
245
- Handles both standalone annotations and those with parent annotations.
200
+ This function modifies the Ff (flags) entry in the annotation dictionary to
201
+ set the ReadOnly flag, preventing the user from interacting with the form field.
246
202
 
247
203
  Args:
248
- annot: PDF annotation dictionary to flatten
204
+ annot (DictionaryObject): The annotation dictionary.
249
205
  """
250
-
251
206
  if Parent in annot and Ff not in annot:
252
207
  annot[NameObject(Parent)][NameObject(Ff)] = NumberObject(
253
208
  int(annot.get(NameObject(Ff), 0)) | READ_ONLY
@@ -259,52 +214,17 @@ def simple_flatten_generic(annot: DictionaryObject) -> None:
259
214
 
260
215
 
261
216
  def update_annotation_name(annot: DictionaryObject, val: str) -> None:
262
- """Update the name/title of a PDF annotation.
217
+ """
218
+ Updates the name of an annotation, setting the T (title) entry.
263
219
 
264
- Modifies the title (T) field of an annotation to set a new name.
265
- Handles both standalone annotations and those with parent annotations.
220
+ This function modifies the T (title) entry in the annotation dictionary to
221
+ change the name or title of the annotation.
266
222
 
267
223
  Args:
268
- annot: PDF annotation dictionary to modify
269
- val: New name/title to set for the annotation
224
+ annot (DictionaryObject): The annotation dictionary.
225
+ val (str): The new name for the annotation.
270
226
  """
271
-
272
227
  if Parent in annot and T not in annot:
273
228
  annot[NameObject(Parent)][NameObject(T)] = TextStringObject(val)
274
229
  else:
275
230
  annot[NameObject(T)] = TextStringObject(val)
276
-
277
-
278
- def update_created_text_field_alignment(annot: DictionaryObject, val: int) -> None:
279
- """Update text alignment for created text field annotations.
280
-
281
- Modifies the alignment (Q) field of a text field annotation created
282
- by the library to set the specified text alignment.
283
-
284
- Args:
285
- annot: PDF text field annotation dictionary to modify
286
- val: Alignment value to set (typically 0=left, 1=center, 2=right)
287
- """
288
-
289
- annot[NameObject(Q)] = NumberObject(val)
290
-
291
-
292
- def update_created_text_field_multiline(annot: DictionaryObject, val: bool) -> None:
293
- """Update multiline flag for created text field annotations.
294
-
295
- Modifies the field flags (Ff) of a text field annotation created by
296
- the library to set or clear the multiline flag based on the input value.
297
-
298
- Args:
299
- annot: PDF text field annotation dictionary to modify
300
- val: Whether to enable multiline (True) or disable (False)
301
- """
302
-
303
- if val:
304
- annot[NameObject(Ff)] = NumberObject(int(annot[NameObject(Ff)]) | MULTILINE)
305
-
306
-
307
- NON_ACRO_FORM_PARAM_TO_FUNC = {
308
- ("TextWidget", "alignment"): update_created_text_field_alignment,
309
- ("TextWidget", "multiline"): update_created_text_field_multiline,
310
- }