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.

PyPDFForm/image.py CHANGED
@@ -1,12 +1,10 @@
1
1
  # -*- coding: utf-8 -*-
2
- """Provides image processing utilities for PDF forms.
3
-
4
- This module contains functions for:
5
- - Rotating images while maintaining quality
6
- - Extracting image dimensions
7
- - Handling image streams and formats
2
+ """
3
+ This module provides functionalities for handling images within PyPDFForm.
8
4
 
9
- Supports common image formats including JPEG, PNG, and others supported by PIL.
5
+ It includes functions for rotating images, retrieving image dimensions, and
6
+ calculating the resolutions for drawing an image on a PDF page, taking into
7
+ account whether to preserve the aspect ratio.
10
8
  """
11
9
 
12
10
  from io import BytesIO
@@ -14,21 +12,26 @@ from typing import Tuple, Union
14
12
 
15
13
  from PIL import Image
16
14
 
15
+ from .constants import Rect
16
+
17
17
 
18
18
  def rotate_image(image_stream: bytes, rotation: Union[float, int]) -> bytes:
19
- """Rotates an image while maintaining original quality and format.
19
+ """
20
+ Rotates an image by a specified angle in degrees.
21
+
22
+ This function takes an image stream as bytes and rotates it using the PIL library.
23
+ The rotation is performed around the center of the image, and the expand=True
24
+ parameter ensures that the entire rotated image is visible, even if it extends
25
+ beyond the original image boundaries.
20
26
 
21
27
  Args:
22
- image_stream: Input image as bytes
23
- rotation: Rotation angle in degrees (can be a float for precise angles)
28
+ image_stream (bytes): The image data as bytes.
29
+ rotation (Union[float, int]): The rotation angle in degrees. Positive values
30
+ rotate the image counterclockwise, while negative values rotate it clockwise.
24
31
 
25
32
  Returns:
26
- bytes: Rotated image as bytes in the original format
27
-
28
- Note:
29
- Automatically expands the canvas to prevent cropping during rotation.
33
+ bytes: The rotated image data as bytes.
30
34
  """
31
-
32
35
  buff = BytesIO()
33
36
  buff.write(image_stream)
34
37
  buff.seek(0)
@@ -48,18 +51,18 @@ def rotate_image(image_stream: bytes, rotation: Union[float, int]) -> bytes:
48
51
 
49
52
 
50
53
  def get_image_dimensions(image_stream: bytes) -> Tuple[float, float]:
51
- """Extracts width and height from an image stream.
54
+ """
55
+ Retrieves the width and height of an image from its byte stream.
56
+
57
+ This function uses the PIL library to open the image from the provided byte stream
58
+ and returns its dimensions (width and height) as a tuple of floats.
52
59
 
53
60
  Args:
54
- image_stream: Input image as bytes
61
+ image_stream (bytes): The image data as bytes.
55
62
 
56
63
  Returns:
57
- Tuple[float, float]: (width, height) in pixels
58
-
59
- Note:
60
- Works with any image format supported by PIL (Pillow)
64
+ Tuple[float, float]: The width and height of the image in pixels.
61
65
  """
62
-
63
66
  buff = BytesIO()
64
67
  buff.write(image_stream)
65
68
  buff.seek(0)
@@ -67,3 +70,50 @@ def get_image_dimensions(image_stream: bytes) -> Tuple[float, float]:
67
70
  image = Image.open(buff)
68
71
 
69
72
  return image.size
73
+
74
+
75
+ def get_draw_image_resolutions(
76
+ widget: dict,
77
+ preserve_aspect_ratio: bool,
78
+ image_width: float,
79
+ image_height: float,
80
+ ) -> Tuple[float, float, float, float]:
81
+ """
82
+ Calculates the position and dimensions for drawing an image on a PDF page.
83
+
84
+ This function determines the x, y coordinates, width, and height for drawing an
85
+ image within a specified widget area on a PDF page. It takes into account whether
86
+ the aspect ratio of the image should be preserved and adjusts the dimensions
87
+ accordingly.
88
+
89
+ Args:
90
+ widget (dict): A dictionary containing the widget's rectangle coordinates
91
+ (x1, y1, x2, y2) under the key "Rect".
92
+ preserve_aspect_ratio (bool): Whether to preserve the aspect ratio of the image.
93
+ If True, the image will be scaled to fit within the widget area while
94
+ maintaining its original aspect ratio.
95
+ image_width (float): The width of the image in pixels.
96
+ image_height (float): The height of the image in pixels.
97
+
98
+ Returns:
99
+ Tuple[float, float, float, float]: A tuple containing the x, y coordinates,
100
+ width, and height of the image to be drawn on the PDF page.
101
+ """
102
+ x = float(widget[Rect][0])
103
+ y = float(widget[Rect][1])
104
+ width = abs(float(widget[Rect][0]) - float(widget[Rect][2]))
105
+ height = abs(float(widget[Rect][1]) - float(widget[Rect][3]))
106
+
107
+ if preserve_aspect_ratio:
108
+ ratio = max(image_width / width, image_height / height)
109
+
110
+ new_width = image_width / ratio
111
+ new_height = image_height / ratio
112
+
113
+ x += abs(new_width - width) / 2
114
+ y += abs(new_height - height) / 2
115
+
116
+ width = new_width
117
+ height = new_height
118
+
119
+ return x, y, width, height
@@ -1,95 +1,103 @@
1
1
  # -*- coding: utf-8 -*-
2
- """Provides base widget middleware for PDF form elements.
3
-
4
- This module contains the Widget base class that defines common functionality
5
- and properties for all PDF form widgets, including:
6
- - Name and value management
7
- - Styling properties (borders, colors)
8
- - Schema generation
9
- - Basic validation
2
+ """
3
+ Module containing base classes for middleware.
4
+
5
+ This module defines the base class for form widgets, which are used to
6
+ represent form fields in a PDF document. The Widget class provides
7
+ common attributes and methods for all form widgets, such as name, value,
8
+ and schema definition.
10
9
  """
11
10
 
12
11
  from typing import Any
13
12
 
14
13
 
15
14
  class Widget:
16
- """Abstract base class for all PDF form widget middleware.
17
-
18
- Provides common interface and functionality for:
19
- - Managing widget names and values
20
- - Handling visual properties (borders, colors)
21
- - Generating JSON schema definitions
22
- - Providing sample values
15
+ """
16
+ Base class for form widget.
23
17
 
24
- Subclasses must implement:
25
- - sample_value property
26
- - Any widget-specific functionality
18
+ The Widget class provides a base implementation for form widgets,
19
+ which are used to represent form fields in a PDF document. It
20
+ defines common attributes and methods for all form widgets, such
21
+ as name, value, and schema definition.
27
22
  """
28
23
 
24
+ SET_ATTR_TRIGGER_HOOK_MAP = {}
25
+
29
26
  def __init__(
30
27
  self,
31
28
  name: str,
32
29
  value: Any = None,
33
30
  ) -> None:
34
- """Initializes a new widget with basic properties.
31
+ """
32
+ Initialize a new widget.
35
33
 
36
34
  Args:
37
- name: Field name/key for the widget
38
- value: Initial value for the widget (default: None)
35
+ name (str): The name of the widget.
36
+ value (Any): The initial value of the widget. Defaults to None.
39
37
  """
40
-
41
38
  super().__init__()
42
39
  self._name = name
43
40
  self._value = value
44
41
  self.desc = None
45
- self.border_color = None
46
- self.background_color = None
47
- self.border_width = None
48
- self.border_style = None
49
- self.dash_array = None
50
- self.render_widget = None
42
+ self.hooks_to_trigger = []
43
+
44
+ def __setattr__(self, name: str, value: Any) -> None:
45
+ """
46
+ Set an attribute on the widget.
47
+
48
+ This method overrides the default __setattr__ method to
49
+ trigger hooks when certain attributes are set.
50
+
51
+ Args:
52
+ name (str): The name of the attribute.
53
+ value (Any): The value of the attribute.
54
+ """
55
+ if name in self.SET_ATTR_TRIGGER_HOOK_MAP and value is not None:
56
+ self.hooks_to_trigger.append((self.SET_ATTR_TRIGGER_HOOK_MAP[name], value))
57
+ super().__setattr__(name, value)
51
58
 
52
59
  @property
53
60
  def name(self) -> str:
54
- """Gets the widget's field name/key.
61
+ """
62
+ Get the name of the widget.
55
63
 
56
64
  Returns:
57
- str: The widget's name as it appears in the PDF form
65
+ str: The name of the widget.
58
66
  """
59
-
60
67
  return self._name
61
68
 
62
69
  @property
63
70
  def value(self) -> Any:
64
- """Gets the widget's current value.
71
+ """
72
+ Get the value of the widget.
65
73
 
66
74
  Returns:
67
- Any: The widget's current value (type depends on widget type)
75
+ Any: The value of the widget.
68
76
  """
69
-
70
77
  return self._value
71
78
 
72
79
  @value.setter
73
80
  def value(self, value: Any) -> None:
74
- """Sets the widget's value.
81
+ """
82
+ Set the value of the widget.
75
83
 
76
84
  Args:
77
- value: New value for the widget (type depends on widget type)
85
+ value (Any): The value to set.
78
86
  """
79
-
80
87
  self._value = value
81
88
 
82
89
  @property
83
90
  def schema_definition(self) -> dict:
84
- """Generates a JSON schema definition for the widget.
91
+ """
92
+ Get the schema definition of the widget.
93
+
94
+ This method returns a dictionary that defines the schema
95
+ for the widget. The schema definition is used to validate
96
+ the widget's value.
85
97
 
86
98
  Returns:
87
- dict: Schema properties including:
88
- - description (if available)
89
- - type constraints
90
- - other widget-specific properties
99
+ dict: The schema definition of the widget.
91
100
  """
92
-
93
101
  result = {}
94
102
 
95
103
  if self.desc is not None:
@@ -99,12 +107,10 @@ class Widget:
99
107
 
100
108
  @property
101
109
  def sample_value(self) -> Any:
102
- """Generates a sample value appropriate for the widget type.
103
-
104
- Returns:
105
- Any: A representative value demonstrating the widget's expected input
110
+ """
111
+ Get a sample value for the widget.
106
112
 
107
113
  Raises:
108
- NotImplementedError: Must be implemented by subclasses
114
+ NotImplementedError: This method must be implemented by subclasses.
109
115
  """
110
116
  raise NotImplementedError
@@ -1,11 +1,9 @@
1
1
  # -*- coding: utf-8 -*-
2
- """Provides middleware for PDF checkbox widgets.
2
+ """
3
+ Module representing a checkbox widget.
3
4
 
4
- This module contains the Checkbox class which handles:
5
- - Checkbox state management (checked/unchecked)
6
- - Button style customization
7
- - Value validation and conversion
8
- - Schema generation for form validation
5
+ This module defines the Checkbox class, which is a subclass of the
6
+ Widget class. It represents a checkbox form field in a PDF document.
9
7
  """
10
8
 
11
9
  from typing import Union
@@ -14,21 +12,16 @@ from .base import Widget
14
12
 
15
13
 
16
14
  class Checkbox(Widget):
17
- """Middleware for PDF checkbox widgets.
18
-
19
- Handles all aspects of checkbox processing including:
20
- - State management (checked/unchecked)
21
- - Button style customization (check, cross, circle)
22
- - Value validation
23
- - PDF form field integration
15
+ """
16
+ Represents a checkbox widget.
24
17
 
25
- Inherits from Widget base class and extends it with checkbox-specific features.
18
+ The Checkbox class provides a concrete implementation for
19
+ checkbox form fields. It inherits from the Widget class and
20
+ implements the schema_definition and sample_value properties.
26
21
  """
27
22
 
28
- BUTTON_STYLE_MAPPING = {
29
- "check": "4",
30
- "cross": "5",
31
- "circle": "l",
23
+ SET_ATTR_TRIGGER_HOOK_MAP = {
24
+ "size": "update_check_radio_size",
32
25
  }
33
26
 
34
27
  def __init__(
@@ -36,62 +29,42 @@ class Checkbox(Widget):
36
29
  name: str,
37
30
  value: bool = None,
38
31
  ) -> None:
39
- """Initializes a new checkbox widget.
32
+ """
33
+ Initializes a checkbox widget.
40
34
 
41
35
  Args:
42
- name: Field name/key for the checkbox
43
- value: Initial checked state (default: None)
44
- """
36
+ name (str): The name of the checkbox.
37
+ value (bool): The initial value of the checkbox. Defaults to None.
45
38
 
39
+ Attributes:
40
+ size (int): The size of the checkbox. Defaults to None.
41
+ """
46
42
  super().__init__(name, value)
47
43
 
48
44
  self.size = None
49
- self._button_style = self.BUTTON_STYLE_MAPPING["check"]
50
45
 
51
46
  @property
52
47
  def schema_definition(self) -> dict:
53
- """Generates a JSON schema definition for the checkbox.
48
+ """
49
+ Returns the schema definition for the checkbox.
50
+
51
+ The schema definition is a dictionary that describes the
52
+ data type and other constraints for the checkbox value.
54
53
 
55
54
  Returns:
56
- dict: Schema properties including:
57
- - type: boolean
58
- - description (if available from base class)
55
+ dict: A dictionary representing the schema definition.
59
56
  """
60
-
61
57
  return {"type": "boolean", **super().schema_definition}
62
58
 
63
59
  @property
64
60
  def sample_value(self) -> Union[bool, int]:
65
- """Generates a sample value for the checkbox.
66
-
67
- Returns:
68
- Union[bool, int]: Always returns True as the sample checked state
69
61
  """
62
+ Returns a sample value for the checkbox.
70
63
 
71
- return True
72
-
73
- @property
74
- def button_style(self) -> Union[str, None]:
75
- """Gets the current button style identifier.
64
+ The sample value is used to generate example data for the
65
+ checkbox field.
76
66
 
77
67
  Returns:
78
- Union[str, None]: The button style code used in PDF form fields
68
+ Union[bool, int]: A sample value for the checkbox.
79
69
  """
80
-
81
- return self._button_style
82
-
83
- @button_style.setter
84
- def button_style(self, value) -> None:
85
- """Sets the button style for the checkbox.
86
-
87
- Accepts either style names ('check', 'cross', 'circle') or
88
- direct PDF button style codes ('4', '5', 'l').
89
-
90
- Args:
91
- value: Button style name or code to set
92
- """
93
-
94
- if value in self.BUTTON_STYLE_MAPPING:
95
- self._button_style = self.BUTTON_STYLE_MAPPING[value]
96
- elif value in self.BUTTON_STYLE_MAPPING.values():
97
- self._button_style = value
70
+ return True
@@ -1,58 +1,68 @@
1
1
  # -*- coding: utf-8 -*-
2
- """Provides middleware for PDF dropdown/combobox widgets.
2
+ """
3
+ Module representing a dropdown widget.
3
4
 
4
- This module contains the Dropdown class which handles:
5
- - Dropdown option management
6
- - Selection index tracking
7
- - Value validation
8
- - Schema generation for form validation
5
+ This module defines the Dropdown class, which is a subclass of the
6
+ Widget class. It represents a dropdown form field in a PDF document.
9
7
  """
10
8
 
11
9
  from .base import Widget
12
10
 
13
11
 
14
12
  class Dropdown(Widget):
15
- """Middleware for PDF dropdown/combobox widgets.
13
+ """
14
+ Represents a dropdown widget in a PDF form.
15
+
16
+ Inherits from the Widget class and provides specific functionality
17
+ for handling dropdown form fields.
16
18
 
17
- Handles all aspects of dropdown processing including:
18
- - Option list management
19
- - Selection index validation
20
- - Value conversion
21
- - PDF form field integration
19
+ Key attributes:
20
+ font (str): The font of the dropdown field.
21
+ choices (List[str]): The list of available options in the dropdown.
22
+ value (int): The index of the selected choice in the choices list.
22
23
 
23
- Inherits from Widget base class and extends it with dropdown-specific features.
24
+ Methods:
25
+ schema_definition: Returns a schema definition for the dropdown's value.
26
+ sample_value: Returns a sample value for the dropdown.
24
27
  """
25
28
 
29
+ SET_ATTR_TRIGGER_HOOK_MAP = {
30
+ "font": "update_text_field_font",
31
+ "choices": "update_dropdown_choices",
32
+ }
33
+
26
34
  def __init__(
27
35
  self,
28
36
  name: str,
29
37
  value: int = None,
30
38
  ) -> None:
31
- """Initializes a new dropdown widget.
39
+ """
40
+ Initializes a dropdown widget.
32
41
 
33
42
  Args:
34
- name: Field name/key for the dropdown
35
- value: Initial selected option index (default: None)
36
- """
43
+ name (str): The name of the dropdown.
44
+ value (int): The initial value of the dropdown. Defaults to None.
37
45
 
46
+ Attributes:
47
+ font (str): The font of the dropdown field.
48
+ choices (List[str]): The list of choices for the dropdown.
49
+ """
38
50
  super().__init__(name, value)
39
51
 
40
- self.choices = []
41
- self.desc = None
52
+ self.font = None
53
+ self.choices = None
42
54
 
43
55
  @property
44
56
  def schema_definition(self) -> dict:
45
- """Generates a JSON schema definition for the dropdown.
57
+ """
58
+ Returns the schema definition for the dropdown.
46
59
 
47
- Includes:
48
- - Type constraint (integer)
49
- - Maximum valid option index
50
- - Any inherited schema properties
60
+ The schema definition is a dictionary that describes the
61
+ data type and other constraints for the dropdown value.
51
62
 
52
63
  Returns:
53
- dict: Complete JSON schema definition
64
+ dict: A dictionary representing the schema definition.
54
65
  """
55
-
56
66
  return {
57
67
  "type": "integer",
58
68
  "maximum": len(self.choices) - 1,
@@ -61,12 +71,13 @@ class Dropdown(Widget):
61
71
 
62
72
  @property
63
73
  def sample_value(self) -> int:
64
- """Generates a sample value for the dropdown.
74
+ """
75
+ Returns a sample value for the dropdown.
65
76
 
66
- Returns the index of the last option by default.
77
+ The sample value is used to generate example data for the
78
+ dropdown field.
67
79
 
68
80
  Returns:
69
- int: Index of the last option in the dropdown
81
+ int: A sample value for the dropdown.
70
82
  """
71
-
72
83
  return len(self.choices) - 1
@@ -1,34 +1,22 @@
1
1
  # -*- coding: utf-8 -*-
2
- """Provides middleware for PDF image field widgets.
3
-
4
- This module contains the Image class which handles:
5
- - Image field processing for common formats (JPEG, PNG)
6
- - Aspect ratio preservation when scaling
7
- - PDF form field integration
8
- - Image rotation and positioning
9
-
10
- Supports image data from:
11
- - Raw bytes
12
- - File paths
13
- - File-like objects
2
+ """
3
+ Module representing an image widget.
14
4
 
15
- Note: Inherits core functionality from Signature middleware since
16
- PDF image fields are technically signature fields with images.
5
+ This module defines the Image class, which is a subclass of the
6
+ Signature class. It represents an image form field in a PDF document,
7
+ allowing users to add images to the form.
17
8
  """
18
9
 
19
10
  from .signature import Signature
20
11
 
21
12
 
22
13
  class Image(Signature):
23
- """Middleware for PDF image field widgets.
24
-
25
- Handles all aspects of image field processing including:
26
- - Image data handling
27
- - Aspect ratio control
28
- - PDF form field integration
14
+ """
15
+ Represents an image widget.
29
16
 
30
- Inherits from Signature class and extends it with image-specific features.
17
+ The Image class provides a concrete implementation for
18
+ image form fields. It inherits from the Signature class and
19
+ sets the preserve_aspect_ratio attribute to False by default.
31
20
  """
32
21
 
33
22
  preserve_aspect_ratio = False
34
- """Whether to preserve the original image's aspect ratio when scaling."""
@@ -1,26 +1,22 @@
1
1
  # -*- coding: utf-8 -*-
2
- """Provides middleware for PDF radio button widgets.
2
+ """
3
+ Module representing a radio button widget.
3
4
 
4
- This module contains the Radio class which handles:
5
- - Radio button group state management
6
- - Option selection tracking
7
- - Value validation and conversion
8
- - Schema generation for form validation
5
+ This module defines the Radio class, which is a subclass of the
6
+ Checkbox class. It represents a radio button form field in a PDF
7
+ document, allowing users to select one option from a group of choices.
9
8
  """
10
9
 
11
10
  from .checkbox import Checkbox
12
11
 
13
12
 
14
13
  class Radio(Checkbox):
15
- """Middleware for PDF radio button widgets.
16
-
17
- Handles all aspects of radio button processing including:
18
- - Group selection management
19
- - Option counting and validation
20
- - Button style customization
21
- - PDF form field integration
14
+ """
15
+ Represents a radio button widget.
22
16
 
23
- Inherits from Checkbox class and extends it with radio-specific features.
17
+ The Radio class provides a concrete implementation for radio button
18
+ form fields. It inherits from the Checkbox class and implements
19
+ the schema_definition and sample_value properties.
24
20
  """
25
21
 
26
22
  def __init__(
@@ -28,32 +24,33 @@ class Radio(Checkbox):
28
24
  name: str,
29
25
  value: int = None,
30
26
  ) -> None:
31
- """Initializes a new radio button widget.
27
+ """
28
+ Initializes a radio button widget.
32
29
 
33
30
  Args:
34
- name: Field name/key for the radio button group
35
- value: Initial selected option index (default: None)
36
- """
31
+ name (str): The name of the radio button.
32
+ value (int): The initial value of the radio button. Defaults to None.
37
33
 
34
+ Attributes:
35
+ number_of_options (int): The number of options for the radio button.
36
+ """
38
37
  super().__init__(name, value)
39
38
 
40
- self.size = None
41
39
  self.number_of_options = 0
42
- self._button_style = self.BUTTON_STYLE_MAPPING["circle"]
43
40
 
44
41
  @property
45
42
  def schema_definition(self) -> dict:
46
- """Generates a JSON schema definition for the radio button group.
43
+ """
44
+ Returns the schema definition for the radio button.
47
45
 
48
- Includes:
49
- - Type constraint (integer)
50
- - Maximum valid option index
51
- - Any inherited schema properties
46
+ The schema definition is a dictionary that describes the
47
+ data type and other constraints for the radio button value,
48
+ which is expected to be an integer representing the index
49
+ of the selected option.
52
50
 
53
51
  Returns:
54
- dict: Complete JSON schema definition
52
+ dict: A dictionary representing the schema definition.
55
53
  """
56
-
57
54
  return {
58
55
  "maximum": self.number_of_options - 1,
59
56
  **super().schema_definition,
@@ -62,12 +59,14 @@ class Radio(Checkbox):
62
59
 
63
60
  @property
64
61
  def sample_value(self) -> int:
65
- """Generates a sample value for the radio button group.
62
+ """
63
+ Returns a sample value for the radio button.
66
64
 
67
- Returns the index of the last option by default.
65
+ The sample value is used to generate example data for the
66
+ radio button field. It returns the index of the last option
67
+ in the group.
68
68
 
69
69
  Returns:
70
- int: Index of the last option in the group
70
+ int: A sample value for the radio button.
71
71
  """
72
-
73
72
  return self.number_of_options - 1