PyPDFForm 2.5.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,35 +1,34 @@
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.
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.
28
23
  """
29
24
 
30
25
  SET_ATTR_TRIGGER_HOOK_MAP = {
26
+ "font": "update_text_field_font",
31
27
  "font_size": "update_text_field_font_size",
32
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",
33
32
  }
34
33
 
35
34
  def __init__(
@@ -37,36 +36,43 @@ class Text(Widget):
37
36
  name: str,
38
37
  value: str = None,
39
38
  ) -> None:
40
- """Initializes a new text field widget.
39
+ """
40
+ Initializes a text field widget.
41
41
 
42
42
  Args:
43
- name: Field name/key for the text field
44
- 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.
45
54
  """
46
-
47
55
  super().__init__(name, value)
48
56
 
49
57
  self.font = None
50
58
  self.font_size = None
51
59
  self.font_color = None
52
- self.text_wrap_length = None
53
- self.max_length = None
54
60
  self.comb = None
55
- self.character_paddings = []
56
- self.text_lines = None
57
- self.text_line_x_coordinates = None
58
- self.preview = False
61
+ self.alignment = None
62
+ self.multiline = None
63
+
64
+ self.max_length = None
59
65
 
60
66
  @property
61
- def value(self) -> Any:
62
- """Gets the text field's current value with type conversion.
67
+ def value(self) -> str:
68
+ """
69
+ Returns the value of the text field.
63
70
 
64
- Converts numeric values to strings automatically.
71
+ If the value is an integer or float, it is converted to a string.
65
72
 
66
73
  Returns:
67
- Any: The text value as a string (converted if numeric)
74
+ str: The value of the text field.
68
75
  """
69
-
70
76
  if isinstance(self._value, (int, float)):
71
77
  return str(self._value)
72
78
 
@@ -74,27 +80,25 @@ class Text(Widget):
74
80
 
75
81
  @value.setter
76
82
  def value(self, value: str) -> None:
77
- """Sets the text field's value.
83
+ """
84
+ Sets the value of the text field.
78
85
 
79
86
  Args:
80
- value: New text value (will be converted to string if numeric)
87
+ value (str): The value to set.
81
88
  """
82
-
83
89
  self._value = value
84
90
 
85
91
  @property
86
92
  def schema_definition(self) -> dict:
87
- """Generates a JSON schema definition for the text field.
93
+ """
94
+ Returns the schema definition for the text field.
88
95
 
89
- Includes:
90
- - Type constraint (string)
91
- - Max length if specified
92
- - 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.
93
98
 
94
99
  Returns:
95
- dict: Complete JSON schema definition
100
+ dict: A dictionary representing the schema definition.
96
101
  """
97
-
98
102
  result = {"type": "string"}
99
103
 
100
104
  if self.max_length is not None:
@@ -104,14 +108,16 @@ class Text(Widget):
104
108
 
105
109
  @property
106
110
  def sample_value(self) -> str:
107
- """Generates a sample value demonstrating the text field's capacity.
111
+ """
112
+ Returns a sample value for the text field.
108
113
 
109
- 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.
110
117
 
111
118
  Returns:
112
- str: Sample text value for demonstration purposes
119
+ str: A sample value for the text field.
113
120
  """
114
-
115
121
  return (
116
122
  self.name[: self.max_length] if self.max_length is not None else self.name
117
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, READ_ONLY, TU, A, Btn,
23
- Ch, D, Ff, I, N, Off, Opt, Parent, Q, S, Sig, T, Tx, V,
24
- 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
98
 
137
- BORDER_DASH_ARRAY_PATTERNS = [{BS: {D: True}}]
138
-
139
-
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,16 +214,16 @@ 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: