PyPDFForm 4.7.10__tar.gz → 4.8.0__tar.gz

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.
Files changed (82) hide show
  1. {pypdfform-4.7.10 → pypdfform-4.8.0}/PKG-INFO +7 -7
  2. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/__init__.py +1 -1
  3. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/cli/__init__.py +58 -1
  4. pypdfform-4.8.0/PyPDFForm/cli/coordinate.py +165 -0
  5. pypdfform-4.8.0/PyPDFForm/cli/create.py +169 -0
  6. pypdfform-4.8.0/PyPDFForm/cli/inspect.py +53 -0
  7. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm.egg-info/PKG-INFO +7 -7
  8. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm.egg-info/SOURCES.txt +2 -0
  9. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm.egg-info/requires.txt +6 -6
  10. {pypdfform-4.7.10 → pypdfform-4.8.0}/pyproject.toml +6 -6
  11. {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_cli.py +24 -0
  12. pypdfform-4.7.10/PyPDFForm/cli/coordinate.py +0 -80
  13. {pypdfform-4.7.10 → pypdfform-4.8.0}/LICENSE +0 -0
  14. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/cli/update.py +0 -0
  15. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/__init__.py +0 -0
  16. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/adapter.py +0 -0
  17. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/annotations/__init__.py +0 -0
  18. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/annotations/base.py +0 -0
  19. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/annotations/link.py +0 -0
  20. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/annotations/stamp.py +0 -0
  21. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/annotations/text.py +0 -0
  22. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/annotations/text_markup.py +0 -0
  23. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/assets/__init__.py +0 -0
  24. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/assets/bedrock.py +0 -0
  25. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/assets/blank.py +0 -0
  26. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/constants.py +0 -0
  27. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/coordinate.py +0 -0
  28. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/deprecation.py +0 -0
  29. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/egress.py +0 -0
  30. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/filler.py +0 -0
  31. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/font.py +0 -0
  32. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/hooks.py +0 -0
  33. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/image.py +0 -0
  34. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/middleware/__init__.py +0 -0
  35. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/middleware/base.py +0 -0
  36. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/middleware/checkbox.py +0 -0
  37. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/middleware/dropdown.py +0 -0
  38. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/middleware/image.py +0 -0
  39. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/middleware/radio.py +0 -0
  40. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/middleware/signature.py +0 -0
  41. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/middleware/text.py +0 -0
  42. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/patterns.py +0 -0
  43. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/raw/__init__.py +0 -0
  44. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/raw/circle.py +0 -0
  45. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/raw/ellipse.py +0 -0
  46. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/raw/image.py +0 -0
  47. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/raw/line.py +0 -0
  48. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/raw/rect.py +0 -0
  49. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/raw/text.py +0 -0
  50. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/template.py +0 -0
  51. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/types.py +0 -0
  52. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/utils.py +0 -0
  53. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/watermark.py +0 -0
  54. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/widgets/__init__.py +0 -0
  55. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/widgets/base.py +0 -0
  56. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/widgets/checkbox.py +0 -0
  57. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/widgets/dropdown.py +0 -0
  58. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/widgets/image.py +0 -0
  59. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/widgets/radio.py +0 -0
  60. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/widgets/signature.py +0 -0
  61. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/widgets/text.py +0 -0
  62. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/wrapper.py +0 -0
  63. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm.egg-info/dependency_links.txt +0 -0
  64. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm.egg-info/entry_points.txt +0 -0
  65. {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm.egg-info/top_level.txt +0 -0
  66. {pypdfform-4.7.10 → pypdfform-4.8.0}/README.md +0 -0
  67. {pypdfform-4.7.10 → pypdfform-4.8.0}/setup.cfg +0 -0
  68. {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_bulk_create_fields.py +0 -0
  69. {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_create_widget.py +0 -0
  70. {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_draw_elements.py +0 -0
  71. {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_dropdown.py +0 -0
  72. {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_extract_middleware_attributes.py +0 -0
  73. {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_fill_max_length_text_field.py +0 -0
  74. {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_font_widths.py +0 -0
  75. {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_functional.py +0 -0
  76. {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_generate_appearance_streams.py +0 -0
  77. {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_js.py +0 -0
  78. {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_need_appearances.py +0 -0
  79. {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_paragraph.py +0 -0
  80. {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_signature.py +0 -0
  81. {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_use_full_widget_name.py +0 -0
  82. {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_widget_attr_trigger.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPDFForm
3
- Version: 4.7.10
3
+ Version: 4.8.0
4
4
  Summary: The Python library for PDF forms.
5
5
  Author: Jinge Li
6
6
  License-Expression: MIT
@@ -21,26 +21,26 @@ Requires-Python: >=3.10
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
23
  Requires-Dist: cryptography<47.0.0,>=46.0.3
24
- Requires-Dist: fonttools<5.0.0,>=4.60.1
24
+ Requires-Dist: fonttools<5.0.0,>=4.62.1
25
25
  Requires-Dist: pikepdf<11.0.0,>=10.5.0
26
26
  Requires-Dist: pillow<13.0.0,>=12.0.0
27
- Requires-Dist: pypdf<6.10.0,>=6.9.0
27
+ Requires-Dist: pypdf<7.0.0,>=6.10.1
28
28
  Requires-Dist: reportlab<5.0.0,>=4.4.6
29
29
  Provides-Extra: cli
30
30
  Requires-Dist: typer<1.0.0,>=0.24.1; extra == "cli"
31
31
  Provides-Extra: dev
32
32
  Requires-Dist: black<27.0.0,>=25.11.0; extra == "dev"
33
33
  Requires-Dist: coverage<8.0.0,>=7.12.0; extra == "dev"
34
- Requires-Dist: isort<9.0.0,>=7.0.0; extra == "dev"
35
- Requires-Dist: jsonschema<5.0.0,>=4.25.1; extra == "dev"
34
+ Requires-Dist: isort<9.0.0,>=8.0.1; extra == "dev"
35
+ Requires-Dist: jsonschema<5.0.0,>=4.26.0; extra == "dev"
36
36
  Requires-Dist: mike<3.0.0,>=2.1.3; extra == "dev"
37
37
  Requires-Dist: mkdocs<2.0.0,>=1.6.1; extra == "dev"
38
38
  Requires-Dist: mkdocs-material<10.0.0,>=9.7.0; extra == "dev"
39
39
  Requires-Dist: pudb<2026.0.0,>=2025.1.3; extra == "dev"
40
40
  Requires-Dist: pylint<5.0.0,>=4.0.3; extra == "dev"
41
41
  Requires-Dist: pyright<2.0.0,>=1.1.407; extra == "dev"
42
- Requires-Dist: pytest<10.0.0,>=9.0.1; extra == "dev"
43
- Requires-Dist: requests<3.0.0,>=2.32.5; extra == "dev"
42
+ Requires-Dist: pytest<10.0.0,>=9.0.3; extra == "dev"
43
+ Requires-Dist: requests<3.0.0,>=2.33.1; extra == "dev"
44
44
  Requires-Dist: ruff<1.0.0,>=0.14.6; extra == "dev"
45
45
  Dynamic: license-file
46
46
 
@@ -22,7 +22,7 @@ PyPDFForm aims to simplify PDF form manipulation, making it accessible to develo
22
22
 
23
23
  import logging
24
24
 
25
- __version__ = "4.7.10"
25
+ __version__ = "4.8.0"
26
26
 
27
27
  from .lib.annotations import Annotations
28
28
  from .lib.assets.blank import BlankPage
@@ -6,12 +6,15 @@ It defines the CLI application using Typer, providing commands for
6
6
  interacting with PyPDFForm functionality from the terminal.
7
7
  """
8
8
 
9
+ import json
9
10
  from typing import Annotated
10
11
 
11
12
  import typer
12
13
 
13
- from .. import __version__
14
+ from .. import PdfWrapper, Widgets, __version__
14
15
  from .coordinate import coordinate_cli
16
+ from .create import create_cli
17
+ from .inspect import inspect_cli
15
18
  from .update import update_cli
16
19
 
17
20
  cli_app = typer.Typer(
@@ -22,6 +25,16 @@ cli_app.add_typer(
22
25
  name="coordinate",
23
26
  help="Subcommands for interacting with PDF coordinates and dimensions.",
24
27
  )
28
+ cli_app.add_typer(
29
+ create_cli,
30
+ name="create",
31
+ help="Subcommands for creating elements on PDF forms.",
32
+ )
33
+ cli_app.add_typer(
34
+ inspect_cli,
35
+ name="inspect",
36
+ help="Subcommands for inspecting PDF forms.",
37
+ )
25
38
  cli_app.add_typer(
26
39
  update_cli,
27
40
  name="update",
@@ -169,4 +182,48 @@ def main(
169
182
  ...
170
183
 
171
184
 
185
+ @cli_app.command(no_args_is_help=True)
186
+ def fill(
187
+ ctx: typer.Context,
188
+ pdf: Annotated[str, typer.Argument(help="Path to the input PDF file.")],
189
+ data: Annotated[
190
+ str,
191
+ typer.Option(
192
+ "--file",
193
+ "-f",
194
+ help="Path to the JSON file representing the filling data.",
195
+ ),
196
+ ],
197
+ output: Annotated[
198
+ str,
199
+ typer.Option(
200
+ "--output",
201
+ "-o",
202
+ help="Path to save the output PDF. Defaults to the original path if not specified.",
203
+ ),
204
+ ] = None,
205
+ flatten: Annotated[
206
+ bool,
207
+ typer.Option(
208
+ "--flatten", help="Whether to flatten the filled PDF form or not."
209
+ ),
210
+ ] = None,
211
+ ) -> None:
212
+ """
213
+ Fill a PDF form.
214
+ """
215
+ with open(data, "r", encoding="utf-8") as f:
216
+ input_data = json.load(f)
217
+
218
+ obj = PdfWrapper(pdf, **ctx.obj)
219
+ for k, each in obj.widgets.items():
220
+ if k in input_data and isinstance(each, (Widgets.Image, Widgets.Signature)):
221
+ each.preserve_aspect_ratio = input_data.get(k, {}).get(
222
+ "preserve_aspect_ratio", each.preserve_aspect_ratio
223
+ )
224
+ input_data[k] = input_data[k]["path"]
225
+
226
+ obj.fill(input_data, flatten=flatten).write(output or pdf)
227
+
228
+
172
229
  __all__ = ["cli_app"]
@@ -0,0 +1,165 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ CLI commands for interacting with PDF coordinates.
4
+
5
+ This module provides command-line interface commands for working with
6
+ PDF coordinates and dimensions, such as generating a coordinate grid view.
7
+ """
8
+
9
+ import json
10
+ from typing import Annotated
11
+
12
+ import typer
13
+
14
+ from .. import PdfWrapper
15
+
16
+ coordinate_cli = typer.Typer(
17
+ context_settings={"help_option_names": ["--help", "-h"]}, no_args_is_help=True
18
+ )
19
+
20
+
21
+ @coordinate_cli.command(no_args_is_help=True)
22
+ def grid(
23
+ ctx: typer.Context,
24
+ pdf: Annotated[str, typer.Argument(help="Path to the input PDF file.")],
25
+ output: Annotated[
26
+ str,
27
+ typer.Option(
28
+ "--output",
29
+ "-o",
30
+ help="Path to save the output PDF. Defaults to the original path if not specified.",
31
+ ),
32
+ ] = None,
33
+ red: Annotated[
34
+ float,
35
+ typer.Option(
36
+ "--red",
37
+ "-r",
38
+ help="Red channel of the RGB color.",
39
+ ),
40
+ ] = None,
41
+ green: Annotated[
42
+ float,
43
+ typer.Option(
44
+ "--green",
45
+ "-g",
46
+ help="Green channel of the RGB color.",
47
+ ),
48
+ ] = None,
49
+ blue: Annotated[
50
+ float,
51
+ typer.Option(
52
+ "--blue",
53
+ "-b",
54
+ help="Blue channel of the RGB color.",
55
+ ),
56
+ ] = None,
57
+ margin: Annotated[
58
+ float,
59
+ typer.Option(
60
+ "--margin",
61
+ "-m",
62
+ help="Margin of the grid view in points.",
63
+ ),
64
+ ] = None,
65
+ ) -> None:
66
+ """
67
+ Generate a coordinate grid view for a PDF.
68
+ """
69
+ params = {}
70
+ if any(
71
+ [
72
+ red is not None,
73
+ green is not None,
74
+ blue is not None,
75
+ ]
76
+ ):
77
+ params["color"] = (red or 0, green or 0, blue or 0)
78
+
79
+ if margin is not None:
80
+ params["margin"] = int(margin) if margin.is_integer() else margin
81
+ PdfWrapper(pdf, **ctx.obj).generate_coordinate_grid(**params).write(output or pdf)
82
+
83
+
84
+ @coordinate_cli.command(no_args_is_help=True)
85
+ def inspect(
86
+ ctx: typer.Context,
87
+ pdf: Annotated[str, typer.Argument(help="Path to the input PDF file.")],
88
+ field: Annotated[
89
+ str, typer.Option("--field", "-f", help="Name of the form field to inspect.")
90
+ ],
91
+ ) -> None:
92
+ """
93
+ Inspect the page number, coordinates, and dimensions of a form field's rectangular bounding box.
94
+ """
95
+ f = PdfWrapper(pdf, **ctx.obj).widgets[field]
96
+
97
+ print(
98
+ json.dumps(
99
+ {
100
+ "page_number": f.page_number,
101
+ "x": f.x,
102
+ "y": f.y,
103
+ "width": f.width,
104
+ "height": f.height,
105
+ }
106
+ )
107
+ )
108
+
109
+
110
+ @coordinate_cli.command(no_args_is_help=True)
111
+ def modify(
112
+ ctx: typer.Context,
113
+ pdf: Annotated[str, typer.Argument(help="Path to the input PDF file.")],
114
+ field: Annotated[
115
+ str, typer.Option("--field", "-f", help="Name of the form field to modify.")
116
+ ],
117
+ output: Annotated[
118
+ str,
119
+ typer.Option(
120
+ "--output",
121
+ "-o",
122
+ help="Path to save the output PDF. Defaults to the original path if not specified.",
123
+ ),
124
+ ] = None,
125
+ x: Annotated[
126
+ float,
127
+ typer.Option(
128
+ "--x",
129
+ help="New x coordinate.",
130
+ ),
131
+ ] = None,
132
+ y: Annotated[
133
+ float,
134
+ typer.Option(
135
+ "--y",
136
+ help="New y coordinate.",
137
+ ),
138
+ ] = None,
139
+ width: Annotated[
140
+ float,
141
+ typer.Option(
142
+ "--width",
143
+ help="New width.",
144
+ ),
145
+ ] = None,
146
+ height: Annotated[
147
+ float,
148
+ typer.Option(
149
+ "--height",
150
+ help="New height.",
151
+ ),
152
+ ] = None,
153
+ ) -> None:
154
+ """
155
+ Modify the coordinates and dimensions of a form field's rectangular bounding box.
156
+ """
157
+ obj = PdfWrapper(pdf, **ctx.obj)
158
+ f = obj.widgets[field]
159
+
160
+ f.x = x if x is not None else f.x
161
+ f.y = y if y is not None else f.y
162
+ f.width = width if width is not None else f.width
163
+ f.height = height if height is not None else f.height
164
+
165
+ obj.write(output or pdf)
@@ -0,0 +1,169 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ CLI module for creating PDF form fields.
4
+
5
+ This module provides command-line interfaces to create various types of PDF form fields
6
+ (such as text fields, checkboxes, radio buttons, dropdowns, signatures, and images)
7
+ in an existing PDF. It aims to mimic the field creation features available via the
8
+ Python API as described in the preparation documentation.
9
+ """
10
+
11
+ import json
12
+ from typing import Annotated
13
+
14
+ import typer
15
+
16
+ from .. import BlankPage, Fields, PdfWrapper, RawElements
17
+
18
+ create_cli = typer.Typer(
19
+ context_settings={"help_option_names": ["--help", "-h"]}, no_args_is_help=True
20
+ )
21
+
22
+
23
+ def _create_elements_from_file(
24
+ pdf: str,
25
+ data: str,
26
+ element_map: dict,
27
+ method_name: str,
28
+ ctx: typer.Context,
29
+ output: str = None,
30
+ ) -> None:
31
+ """
32
+ Create PDF elements from a JSON file.
33
+
34
+ Args:
35
+ pdf: Path to the input PDF file.
36
+ data: Path to the JSON file containing element parameters.
37
+ element_map: Mapping of element type names to element classes.
38
+ method_name: Name of the method to call on PdfWrapper (e.g., "bulk_create_fields", "draw").
39
+ ctx: Typer context containing configuration options.
40
+ output: Path to save the output PDF. Defaults to the original path if not specified.
41
+ """
42
+ with open(data, "r", encoding="utf-8") as f:
43
+ input_data = json.load(f)
44
+
45
+ obj = PdfWrapper(pdf, **ctx.obj)
46
+ ungrouped_input = []
47
+ registered_font = {}
48
+ for k, v in input_data.items():
49
+ for each in v:
50
+ if "font" in each:
51
+ if each["font"] not in registered_font:
52
+ font_name = f"new_font_{len(registered_font)}"
53
+ obj.register_font(font_name, each["font"])
54
+ registered_font[each["font"]] = font_name
55
+ each["font"] = registered_font[each["font"]]
56
+ ungrouped_input.append(element_map[k](**each))
57
+
58
+ getattr(obj, method_name)(ungrouped_input).write(output or pdf)
59
+
60
+
61
+ @create_cli.command(no_args_is_help=True)
62
+ def field(
63
+ ctx: typer.Context,
64
+ pdf: Annotated[str, typer.Argument(help="Path to the input PDF file.")],
65
+ data: Annotated[
66
+ str,
67
+ typer.Option(
68
+ "--file",
69
+ "-f",
70
+ help="Path to the JSON file representing the field creation parameters.",
71
+ ),
72
+ ],
73
+ output: Annotated[
74
+ str,
75
+ typer.Option(
76
+ "--output",
77
+ "-o",
78
+ help="Path to save the output PDF. Defaults to the original path if not specified.",
79
+ ),
80
+ ] = None,
81
+ ) -> None:
82
+ """
83
+ Create PDF form fields.
84
+ """
85
+ field_map = {
86
+ "text": Fields.TextField,
87
+ "check": Fields.CheckBoxField,
88
+ "radio": Fields.RadioGroup,
89
+ "dropdown": Fields.DropdownField,
90
+ "image": Fields.ImageField,
91
+ "signature": Fields.SignatureField,
92
+ }
93
+ _create_elements_from_file(pdf, data, field_map, "bulk_create_fields", ctx, output)
94
+
95
+
96
+ @create_cli.command(no_args_is_help=True)
97
+ def raw(
98
+ ctx: typer.Context,
99
+ pdf: Annotated[str, typer.Argument(help="Path to the input PDF file.")],
100
+ data: Annotated[
101
+ str,
102
+ typer.Option(
103
+ "--file",
104
+ "-f",
105
+ help="Path to the JSON file representing the draw parameters.",
106
+ ),
107
+ ],
108
+ output: Annotated[
109
+ str,
110
+ typer.Option(
111
+ "--output",
112
+ "-o",
113
+ help="Path to save the output PDF. Defaults to the original path if not specified.",
114
+ ),
115
+ ] = None,
116
+ ) -> None:
117
+ """
118
+ Draw raw PDF elements.
119
+ """
120
+ raw_element_map = {
121
+ "text": RawElements.RawText,
122
+ "image": RawElements.RawImage,
123
+ "line": RawElements.RawLine,
124
+ "rectangle": RawElements.RawRectangle,
125
+ "circle": RawElements.RawCircle,
126
+ "ellipse": RawElements.RawEllipse,
127
+ }
128
+ _create_elements_from_file(pdf, data, raw_element_map, "draw", ctx, output)
129
+
130
+
131
+ @create_cli.command(no_args_is_help=True)
132
+ def blank(
133
+ ctx: typer.Context,
134
+ output: Annotated[
135
+ str,
136
+ typer.Option(
137
+ "--output",
138
+ "-o",
139
+ help="Path to save the output PDF.",
140
+ ),
141
+ ],
142
+ count: Annotated[
143
+ int, typer.Option("--count", "-c", help="Number of blank pages.")
144
+ ] = None,
145
+ width: Annotated[
146
+ float,
147
+ typer.Option(
148
+ "--width",
149
+ help="Width of the blank PDF.",
150
+ ),
151
+ ] = None,
152
+ height: Annotated[
153
+ float, typer.Option("--height", help="Height of the blank PDF.")
154
+ ] = None,
155
+ ) -> None:
156
+ """
157
+ Create a new blank PDF.
158
+ """
159
+ params = {}
160
+ if width is not None:
161
+ params["width"] = width
162
+ if height is not None:
163
+ params["height"] = height
164
+
165
+ obj = BlankPage(**params)
166
+ if count is not None and count > 1:
167
+ obj = BlankPage(**params) * count
168
+
169
+ PdfWrapper(obj, **ctx.obj).write(output)
@@ -0,0 +1,53 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ CLI commands for inspecting PDF form field data.
4
+
5
+ This module provides command-line interface commands for extracting
6
+ information from PDF forms. Features include generating a JSON schema
7
+ describing the form fields, inspecting the current filled data of a
8
+ PDF form, and generating sample data for filling a form.
9
+ """
10
+
11
+ import json
12
+ from typing import Annotated
13
+
14
+ import typer
15
+
16
+ from .. import PdfWrapper
17
+
18
+ inspect_cli = typer.Typer(
19
+ context_settings={"help_option_names": ["--help", "-h"]}, no_args_is_help=True
20
+ )
21
+
22
+
23
+ @inspect_cli.command(no_args_is_help=True)
24
+ def schema(
25
+ ctx: typer.Context,
26
+ pdf: Annotated[str, typer.Argument(help="Path to the input PDF file.")],
27
+ ) -> None:
28
+ """
29
+ Generate a JSON schema that describes a PDF form.
30
+ """
31
+ print(json.dumps(PdfWrapper(pdf, **ctx.obj).schema))
32
+
33
+
34
+ @inspect_cli.command(no_args_is_help=True)
35
+ def data(
36
+ ctx: typer.Context,
37
+ pdf: Annotated[str, typer.Argument(help="Path to the input PDF file.")],
38
+ ) -> None:
39
+ """
40
+ Inspect the current filled data of a PDF form.
41
+ """
42
+ print(json.dumps(PdfWrapper(pdf, **ctx.obj).data))
43
+
44
+
45
+ @inspect_cli.command(no_args_is_help=True)
46
+ def sample(
47
+ ctx: typer.Context,
48
+ pdf: Annotated[str, typer.Argument(help="Path to the input PDF file.")],
49
+ ) -> None:
50
+ """
51
+ Generate sample data for filling a PDF form.
52
+ """
53
+ print(json.dumps(PdfWrapper(pdf, **ctx.obj).sample_data))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPDFForm
3
- Version: 4.7.10
3
+ Version: 4.8.0
4
4
  Summary: The Python library for PDF forms.
5
5
  Author: Jinge Li
6
6
  License-Expression: MIT
@@ -21,26 +21,26 @@ Requires-Python: >=3.10
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
23
  Requires-Dist: cryptography<47.0.0,>=46.0.3
24
- Requires-Dist: fonttools<5.0.0,>=4.60.1
24
+ Requires-Dist: fonttools<5.0.0,>=4.62.1
25
25
  Requires-Dist: pikepdf<11.0.0,>=10.5.0
26
26
  Requires-Dist: pillow<13.0.0,>=12.0.0
27
- Requires-Dist: pypdf<6.10.0,>=6.9.0
27
+ Requires-Dist: pypdf<7.0.0,>=6.10.1
28
28
  Requires-Dist: reportlab<5.0.0,>=4.4.6
29
29
  Provides-Extra: cli
30
30
  Requires-Dist: typer<1.0.0,>=0.24.1; extra == "cli"
31
31
  Provides-Extra: dev
32
32
  Requires-Dist: black<27.0.0,>=25.11.0; extra == "dev"
33
33
  Requires-Dist: coverage<8.0.0,>=7.12.0; extra == "dev"
34
- Requires-Dist: isort<9.0.0,>=7.0.0; extra == "dev"
35
- Requires-Dist: jsonschema<5.0.0,>=4.25.1; extra == "dev"
34
+ Requires-Dist: isort<9.0.0,>=8.0.1; extra == "dev"
35
+ Requires-Dist: jsonschema<5.0.0,>=4.26.0; extra == "dev"
36
36
  Requires-Dist: mike<3.0.0,>=2.1.3; extra == "dev"
37
37
  Requires-Dist: mkdocs<2.0.0,>=1.6.1; extra == "dev"
38
38
  Requires-Dist: mkdocs-material<10.0.0,>=9.7.0; extra == "dev"
39
39
  Requires-Dist: pudb<2026.0.0,>=2025.1.3; extra == "dev"
40
40
  Requires-Dist: pylint<5.0.0,>=4.0.3; extra == "dev"
41
41
  Requires-Dist: pyright<2.0.0,>=1.1.407; extra == "dev"
42
- Requires-Dist: pytest<10.0.0,>=9.0.1; extra == "dev"
43
- Requires-Dist: requests<3.0.0,>=2.32.5; extra == "dev"
42
+ Requires-Dist: pytest<10.0.0,>=9.0.3; extra == "dev"
43
+ Requires-Dist: requests<3.0.0,>=2.33.1; extra == "dev"
44
44
  Requires-Dist: ruff<1.0.0,>=0.14.6; extra == "dev"
45
45
  Dynamic: license-file
46
46
 
@@ -10,6 +10,8 @@ PyPDFForm.egg-info/requires.txt
10
10
  PyPDFForm.egg-info/top_level.txt
11
11
  PyPDFForm/cli/__init__.py
12
12
  PyPDFForm/cli/coordinate.py
13
+ PyPDFForm/cli/create.py
14
+ PyPDFForm/cli/inspect.py
13
15
  PyPDFForm/cli/update.py
14
16
  PyPDFForm/lib/__init__.py
15
17
  PyPDFForm/lib/adapter.py
@@ -1,8 +1,8 @@
1
1
  cryptography<47.0.0,>=46.0.3
2
- fonttools<5.0.0,>=4.60.1
2
+ fonttools<5.0.0,>=4.62.1
3
3
  pikepdf<11.0.0,>=10.5.0
4
4
  pillow<13.0.0,>=12.0.0
5
- pypdf<6.10.0,>=6.9.0
5
+ pypdf<7.0.0,>=6.10.1
6
6
  reportlab<5.0.0,>=4.4.6
7
7
 
8
8
  [cli]
@@ -11,14 +11,14 @@ typer<1.0.0,>=0.24.1
11
11
  [dev]
12
12
  black<27.0.0,>=25.11.0
13
13
  coverage<8.0.0,>=7.12.0
14
- isort<9.0.0,>=7.0.0
15
- jsonschema<5.0.0,>=4.25.1
14
+ isort<9.0.0,>=8.0.1
15
+ jsonschema<5.0.0,>=4.26.0
16
16
  mike<3.0.0,>=2.1.3
17
17
  mkdocs<2.0.0,>=1.6.1
18
18
  mkdocs-material<10.0.0,>=9.7.0
19
19
  pudb<2026.0.0,>=2025.1.3
20
20
  pylint<5.0.0,>=4.0.3
21
21
  pyright<2.0.0,>=1.1.407
22
- pytest<10.0.0,>=9.0.1
23
- requests<3.0.0,>=2.32.5
22
+ pytest<10.0.0,>=9.0.3
23
+ requests<3.0.0,>=2.33.1
24
24
  ruff<1.0.0,>=0.14.6
@@ -26,10 +26,10 @@ classifiers = [
26
26
  requires-python = ">=3.10"
27
27
  dependencies = [
28
28
  "cryptography>=46.0.3,<47.0.0",
29
- "fonttools>=4.60.1,<5.0.0",
29
+ "fonttools>=4.62.1,<5.0.0",
30
30
  "pikepdf>=10.5.0,<11.0.0",
31
31
  "pillow>=12.0.0,<13.0.0",
32
- "pypdf>=6.9.0,<6.10.0", # TODO: revert this
32
+ "pypdf>=6.10.1,<7.0.0",
33
33
  "reportlab>=4.4.6,<5.0.0",
34
34
  ]
35
35
 
@@ -42,16 +42,16 @@ cli = ["typer>=0.24.1,<1.0.0"]
42
42
  dev = [
43
43
  "black>=25.11.0,<27.0.0",
44
44
  "coverage>=7.12.0,<8.0.0",
45
- "isort>=7.0.0,<9.0.0",
46
- "jsonschema>=4.25.1,<5.0.0",
45
+ "isort>=8.0.1,<9.0.0",
46
+ "jsonschema>=4.26.0,<5.0.0",
47
47
  "mike>=2.1.3,<3.0.0",
48
48
  "mkdocs>=1.6.1,<2.0.0",
49
49
  "mkdocs-material>=9.7.0,<10.0.0",
50
50
  "pudb>=2025.1.3,<2026.0.0",
51
51
  "pylint>=4.0.3,<5.0.0",
52
52
  "pyright>=1.1.407,<2.0.0",
53
- "pytest>=9.0.1,<10.0.0",
54
- "requests>=2.32.5,<3.0.0",
53
+ "pytest>=9.0.3,<10.0.0",
54
+ "requests>=2.33.1,<3.0.0",
55
55
  "ruff>=0.14.6,<1.0.0",
56
56
  ]
57
57
 
@@ -31,6 +31,14 @@ def test_root_command_with_version():
31
31
  assert long.output == short.output
32
32
 
33
33
 
34
+ @pytest.mark.cli_test
35
+ def test_fill_command():
36
+ result = runner.invoke(cli_app, ["fill"])
37
+ assert result.exit_code == 2
38
+
39
+ assert "Usage:" in result.output
40
+
41
+
34
42
  @pytest.mark.cli_test
35
43
  def test_coordinate_command():
36
44
  result = runner.invoke(cli_app, ["coordinate"])
@@ -39,6 +47,22 @@ def test_coordinate_command():
39
47
  assert "Usage:" in result.output
40
48
 
41
49
 
50
+ @pytest.mark.cli_test
51
+ def test_create_command():
52
+ result = runner.invoke(cli_app, ["create"])
53
+ assert result.exit_code == 2
54
+
55
+ assert "Usage:" in result.output
56
+
57
+
58
+ @pytest.mark.cli_test
59
+ def test_inspect_command():
60
+ result = runner.invoke(cli_app, ["inspect"])
61
+ assert result.exit_code == 2
62
+
63
+ assert "Usage:" in result.output
64
+
65
+
42
66
  @pytest.mark.cli_test
43
67
  def test_update_command():
44
68
  result = runner.invoke(cli_app, ["update"])
@@ -1,80 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- CLI commands for interacting with PDF coordinates.
4
-
5
- This module provides command-line interface commands for working with
6
- PDF coordinates and dimensions, such as generating a coordinate grid view.
7
- """
8
-
9
- from typing import Annotated
10
-
11
- import typer
12
-
13
- from .. import PdfWrapper
14
-
15
- coordinate_cli = typer.Typer(
16
- context_settings={"help_option_names": ["--help", "-h"]}, no_args_is_help=True
17
- )
18
-
19
-
20
- @coordinate_cli.command(no_args_is_help=True)
21
- def grid(
22
- ctx: typer.Context,
23
- pdf: Annotated[str, typer.Argument(help="Path to the input PDF file.")],
24
- output: Annotated[
25
- str,
26
- typer.Option(
27
- "--output",
28
- "-o",
29
- help="Path to save the output PDF. Defaults to the original path if not specified.",
30
- ),
31
- ] = None,
32
- red: Annotated[
33
- float,
34
- typer.Option(
35
- "--red",
36
- "-r",
37
- help="Red channel of the RGB color.",
38
- ),
39
- ] = None,
40
- green: Annotated[
41
- float,
42
- typer.Option(
43
- "--green",
44
- "-g",
45
- help="Green channel of the RGB color.",
46
- ),
47
- ] = None,
48
- blue: Annotated[
49
- float,
50
- typer.Option(
51
- "--blue",
52
- "-b",
53
- help="Blue channel of the RGB color.",
54
- ),
55
- ] = None,
56
- margin: Annotated[
57
- float,
58
- typer.Option(
59
- "--margin",
60
- "-m",
61
- help="Margin of the grid view in points.",
62
- ),
63
- ] = None,
64
- ) -> None:
65
- """
66
- Generate a coordinate grid view for a PDF.
67
- """
68
- params = {}
69
- if any(
70
- [
71
- red is not None,
72
- green is not None,
73
- blue is not None,
74
- ]
75
- ):
76
- params["color"] = (red or 0, green or 0, blue or 0)
77
-
78
- if margin is not None:
79
- params["margin"] = int(margin) if margin.is_integer() else margin
80
- PdfWrapper(pdf, **ctx.obj).generate_coordinate_grid(**params).write(output or pdf)
File without changes
File without changes
File without changes
File without changes