PyPDFForm 5.0.0__tar.gz → 5.1.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 (94) hide show
  1. {pypdfform-5.0.0 → pypdfform-5.1.0}/PKG-INFO +32 -21
  2. pypdfform-5.1.0/PyPDFForm/__init__.py +46 -0
  3. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/cli/common.py +26 -18
  4. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/cli/entry.py +4 -2
  5. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/cli/inspect.py +2 -5
  6. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/cli/schemas/create.py +1 -0
  7. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/cli/schemas/update.py +1 -0
  8. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/cli/update.py +5 -7
  9. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/raw/image.py +1 -0
  10. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/raw/rect.py +1 -0
  11. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/widgets/dropdown.py +1 -0
  12. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/widgets/signature.py +1 -0
  13. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm.egg-info/PKG-INFO +32 -21
  14. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm.egg-info/SOURCES.txt +0 -8
  15. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm.egg-info/requires.txt +5 -8
  16. {pypdfform-5.0.0 → pypdfform-5.1.0}/README.md +20 -11
  17. {pypdfform-5.0.0 → pypdfform-5.1.0}/pyproject.toml +13 -11
  18. pypdfform-5.0.0/PyPDFForm/__init__.py +0 -50
  19. pypdfform-5.0.0/PyPDFForm/api/__init__.py +0 -28
  20. pypdfform-5.0.0/PyPDFForm/api/common.py +0 -101
  21. pypdfform-5.0.0/PyPDFForm/api/create.py +0 -75
  22. pypdfform-5.0.0/PyPDFForm/api/inspect.py +0 -127
  23. pypdfform-5.0.0/PyPDFForm/api/root.py +0 -20
  24. pypdfform-5.0.0/PyPDFForm/api/update.py +0 -56
  25. pypdfform-5.0.0/PyPDFForm/shared/__init__.py +0 -0
  26. pypdfform-5.0.0/PyPDFForm/shared/utils.py +0 -42
  27. {pypdfform-5.0.0 → pypdfform-5.1.0}/LICENSE +0 -0
  28. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/cli/__init__.py +0 -0
  29. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/cli/create.py +0 -0
  30. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/cli/root.py +0 -0
  31. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/cli/schemas/__init__.py +0 -0
  32. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/__init__.py +0 -0
  33. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/adapter.py +0 -0
  34. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/annotations/__init__.py +0 -0
  35. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/annotations/base.py +0 -0
  36. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/annotations/link.py +0 -0
  37. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/annotations/stamp.py +0 -0
  38. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/annotations/text.py +0 -0
  39. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/annotations/text_markup.py +0 -0
  40. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/assets/__init__.py +0 -0
  41. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/assets/bedrock.py +0 -0
  42. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/assets/blank.py +0 -0
  43. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/constants.py +0 -0
  44. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/coordinate.py +0 -0
  45. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/deprecation.py +0 -0
  46. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/egress.py +0 -0
  47. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/filler.py +0 -0
  48. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/font.py +0 -0
  49. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/hooks.py +0 -0
  50. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/image.py +0 -0
  51. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/middleware/__init__.py +0 -0
  52. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/middleware/base.py +0 -0
  53. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/middleware/checkbox.py +0 -0
  54. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/middleware/dropdown.py +0 -0
  55. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/middleware/image.py +0 -0
  56. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/middleware/radio.py +0 -0
  57. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/middleware/signature.py +0 -0
  58. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/middleware/text.py +0 -0
  59. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/patterns.py +0 -0
  60. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/raw/__init__.py +0 -0
  61. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/raw/circle.py +0 -0
  62. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/raw/ellipse.py +0 -0
  63. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/raw/line.py +0 -0
  64. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/raw/text.py +0 -0
  65. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/template.py +0 -0
  66. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/types.py +0 -0
  67. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/utils.py +0 -0
  68. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/watermark.py +0 -0
  69. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/widgets/__init__.py +0 -0
  70. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/widgets/base.py +0 -0
  71. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/widgets/checkbox.py +0 -0
  72. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/widgets/image.py +0 -0
  73. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/widgets/radio.py +0 -0
  74. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/widgets/text.py +0 -0
  75. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm/lib/wrapper.py +0 -0
  76. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm.egg-info/dependency_links.txt +0 -0
  77. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm.egg-info/entry_points.txt +0 -0
  78. {pypdfform-5.0.0 → pypdfform-5.1.0}/PyPDFForm.egg-info/top_level.txt +0 -0
  79. {pypdfform-5.0.0 → pypdfform-5.1.0}/setup.cfg +0 -0
  80. {pypdfform-5.0.0 → pypdfform-5.1.0}/tests/test_bulk_create_fields.py +0 -0
  81. {pypdfform-5.0.0 → pypdfform-5.1.0}/tests/test_create_widget.py +0 -0
  82. {pypdfform-5.0.0 → pypdfform-5.1.0}/tests/test_draw_elements.py +0 -0
  83. {pypdfform-5.0.0 → pypdfform-5.1.0}/tests/test_dropdown.py +0 -0
  84. {pypdfform-5.0.0 → pypdfform-5.1.0}/tests/test_extract_middleware_attributes.py +0 -0
  85. {pypdfform-5.0.0 → pypdfform-5.1.0}/tests/test_fill_max_length_text_field.py +0 -0
  86. {pypdfform-5.0.0 → pypdfform-5.1.0}/tests/test_font_widths.py +0 -0
  87. {pypdfform-5.0.0 → pypdfform-5.1.0}/tests/test_functional.py +0 -0
  88. {pypdfform-5.0.0 → pypdfform-5.1.0}/tests/test_generate_appearance_streams.py +0 -0
  89. {pypdfform-5.0.0 → pypdfform-5.1.0}/tests/test_js.py +0 -0
  90. {pypdfform-5.0.0 → pypdfform-5.1.0}/tests/test_need_appearances.py +0 -0
  91. {pypdfform-5.0.0 → pypdfform-5.1.0}/tests/test_paragraph.py +0 -0
  92. {pypdfform-5.0.0 → pypdfform-5.1.0}/tests/test_signature.py +0 -0
  93. {pypdfform-5.0.0 → pypdfform-5.1.0}/tests/test_use_full_widget_name.py +0 -0
  94. {pypdfform-5.0.0 → pypdfform-5.1.0}/tests/test_widget_attr_trigger.py +0 -0
@@ -1,13 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPDFForm
3
- Version: 5.0.0
4
- Summary: The Python library for PDF forms.
3
+ Version: 5.1.0
4
+ Summary: The Python library & CLI for PDF forms.
5
5
  Author: Jinge Li
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/chinapandaman/PyPDFForm
8
8
  Project-URL: Documentation, https://chinapandaman.github.io/PyPDFForm/
9
9
  Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Environment :: Console
10
11
  Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
11
13
  Classifier: Programming Language :: Python :: 3
12
14
  Classifier: Programming Language :: Python :: 3 :: Only
13
15
  Classifier: Programming Language :: Python :: 3.10
@@ -15,24 +17,24 @@ Classifier: Programming Language :: Python :: 3.11
15
17
  Classifier: Programming Language :: Python :: 3.12
16
18
  Classifier: Programming Language :: Python :: 3.13
17
19
  Classifier: Programming Language :: Python :: 3.14
18
- Classifier: Operating System :: OS Independent
20
+ Classifier: Topic :: File Formats
19
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Text Processing
23
+ Classifier: Topic :: Utilities
20
24
  Requires-Python: >=3.10
21
25
  Description-Content-Type: text/markdown
22
26
  License-File: LICENSE
23
27
  Requires-Dist: cryptography<49.0.0,>=48.0.0
24
- Requires-Dist: fonttools<5.0.0,>=4.62.1
28
+ Requires-Dist: fonttools<5.0.0,>=4.63.0
25
29
  Requires-Dist: pikepdf<11.0.0,>=10.5.1
26
30
  Requires-Dist: pillow<13.0.0,>=12.2.0
27
31
  Requires-Dist: pypdf<7.0.0,>=6.11.0
28
- Requires-Dist: reportlab<5.0.0,>=4.5.0
32
+ Requires-Dist: reportlab<5.0.0,>=4.5.1
29
33
  Provides-Extra: cli
30
34
  Requires-Dist: typer<1.0.0,>=0.25.1; extra == "cli"
31
35
  Requires-Dist: jsonschema<5.0.0,>=4.26.0; extra == "cli"
32
- Provides-Extra: web-api
33
- Requires-Dist: fastapi[standard]<1.0.0,>=0.136.1; extra == "web-api"
34
36
  Provides-Extra: dev
35
- Requires-Dist: black<27.0.0,>=26.3.1; extra == "dev"
37
+ Requires-Dist: black<27.0.0,>=26.5.1; extra == "dev"
36
38
  Requires-Dist: coverage<8.0.0,>=7.14.0; extra == "dev"
37
39
  Requires-Dist: isort<9.0.0,>=8.0.1; extra == "dev"
38
40
  Requires-Dist: mike<3.0.0,>=2.2.0; extra == "dev"
@@ -42,13 +44,13 @@ Requires-Dist: pudb<2026.0.0,>=2025.1.5; extra == "dev"
42
44
  Requires-Dist: pylint<5.0.0,>=4.0.5; extra == "dev"
43
45
  Requires-Dist: pyright<2.0.0,>=1.1.409; extra == "dev"
44
46
  Requires-Dist: pytest<10.0.0,>=9.0.3; extra == "dev"
45
- Requires-Dist: requests<3.0.0,>=2.34.0; extra == "dev"
46
- Requires-Dist: ruff<1.0.0,>=0.15.12; extra == "dev"
47
+ Requires-Dist: requests<3.0.0,>=2.34.2; extra == "dev"
48
+ Requires-Dist: ruff<1.0.0,>=0.15.13; extra == "dev"
47
49
  Dynamic: license-file
48
50
 
49
51
  <p align="center"><img src="https://github.com/chinapandaman/PyPDFForm/raw/master/docs/img/logo.png"></p>
50
52
  <p align="center">
51
- <em>PDF Form Automation Simplified Create, Merge, Style, and Fill Forms Programmatically.</em>
53
+ <em>PDF Form Automation Simplified - Create, Inspect, Style, and Fill Forms in Python or from the Command Line.</em>
52
54
  </p>
53
55
  <p align="center">
54
56
  <a href="https://pypi.org/project/PyPDFForm/"><img src="https://img.shields.io/pypi/v/pypdfform?label=version&color=magenta"></a>
@@ -61,26 +63,36 @@ Dynamic: license-file
61
63
 
62
64
  ## Introduction
63
65
 
64
- PyPDFForm is a Python library for PDF form processing. It contains the essential functionalities needed to interact with PDF forms:
66
+ PyPDFForm is a Python library and command line tool for working with PDF forms. It provides Python APIs and CLI commands for creating, inspecting, updating, and filling forms, plus common PDF utilities.
67
+
68
+ With PyPDFForm, you can:
65
69
 
66
- * Inspect what data a PDF form needs to be filled with.
67
- * Fill a PDF form by simply creating a Python dictionary.
68
- * Create form fields on a PDF.
70
+ * Create PDF forms, form fields, and raw elements.
71
+ * Inspect form fields, metadata, and values.
72
+ * Update field styling, behavior, and scripts.
73
+ * Fill PDF forms.
74
+ * Extract pages and merge PDFs.
69
75
 
70
- It also supports other common utilities such as extracting pages and merging multiple PDFs together.
76
+ The goal is to make PDF form work straightforward, whether you are handling one document or building a larger workflow.
71
77
 
72
78
  ## Installing
73
79
 
74
- Install using [pip](https://pypi.org/project/PyPDFForm/):
80
+ To use PyPDFForm as a Python library, install the base package with [pip](https://pypi.org/project/PyPDFForm/):
75
81
 
76
- ```shell script
82
+ ```shell
77
83
  pip install PyPDFForm
78
84
  ```
79
85
 
86
+ To use the CLI, install PyPDFForm with the `cli` extra using [pipx](https://pipx.pypa.io/stable/):
87
+
88
+ ```shell
89
+ pipx install "PyPDFForm[cli]"
90
+ ```
91
+
80
92
  ## Quick Example
81
93
  ![Check out the GitHub repository for a live demo if you can't see it here.](https://github.com/chinapandaman/PyPDFForm/raw/master/docs/img/demo.gif)
82
94
 
83
- A sample PDF form can be found [here](https://chinapandaman.github.io/PyPDFForm/latest/pdfs/sample_template.pdf). Download it and try:
95
+ The GIF above shows the CLI filling a PDF form. To try the same workflow with the Python library, download the [sample PDF form](https://chinapandaman.github.io/PyPDFForm/latest/pdfs/sample_template.pdf) and run:
84
96
 
85
97
  ```python
86
98
  from PyPDFForm import PdfWrapper
@@ -99,8 +111,7 @@ filled = PdfWrapper("sample_template.pdf", need_appearances=True).fill(
99
111
  filled.write("output.pdf")
100
112
  ```
101
113
 
102
- After running the above code snippet you can find `output.pdf` at the location you specified,
103
- and it should look like [this](https://chinapandaman.github.io/PyPDFForm/latest/pdfs/sample_filled.pdf).
114
+ After running this snippet, `output.pdf` will be written to the location you specified and should look like [this](https://chinapandaman.github.io/PyPDFForm/latest/pdfs/sample_filled.pdf).
104
115
 
105
116
  ## Documentation
106
117
 
@@ -0,0 +1,46 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ PyPDFForm provides Python APIs and CLI commands for working with PDF forms.
4
+
5
+ It helps users create, inspect, update, and fill PDF forms, plus handle common
6
+ PDF utilities such as extracting pages and merging documents.
7
+
8
+ The project supports PDF form features including:
9
+
10
+ - Text, checkbox, radio, dropdown, signature, and image fields.
11
+ - Field styling, sizing, positioning, visibility, and editability.
12
+ - Form data inspection and JSON schema generation.
13
+ - PDF annotations, raw drawing elements, metadata, scripts, and versions.
14
+
15
+ PyPDFForm aims to make PDF form automation straightforward whether it is used
16
+ from Python code or from the command line.
17
+ """
18
+
19
+ import logging
20
+
21
+ __version__ = "5.1.0"
22
+
23
+ from .lib.annotations import Annotations
24
+ from .lib.assets.blank import BlankPage
25
+ from .lib.middleware import Widgets
26
+ from .lib.raw import RawElements
27
+ from .lib.types import PdfArray
28
+ from .lib.widgets import Fields
29
+ from .lib.wrapper import PdfWrapper
30
+
31
+ # TODO: figure out why `Annotation sizes differ:`
32
+ for logger in [
33
+ logging.getLogger(name) for name in getattr(logging.root.manager, "loggerDict")
34
+ ]:
35
+ if "pypdf" in logger.name:
36
+ logger.setLevel(logging.ERROR)
37
+
38
+ __all__ = [
39
+ "PdfWrapper",
40
+ "PdfArray",
41
+ "Annotations",
42
+ "Fields",
43
+ "BlankPage",
44
+ "RawElements",
45
+ "Widgets",
46
+ ]
@@ -15,7 +15,7 @@ import typer
15
15
  from jsonschema import ValidationError, validate
16
16
 
17
17
  from .. import PdfWrapper
18
- from ..shared.utils import WidgetKeyErrorHandler
18
+ from ..lib.middleware.base import Widget
19
19
 
20
20
  INPUT_PDF = Annotated[
21
21
  Path,
@@ -100,23 +100,6 @@ def cli_bad_parameter(
100
100
  raise typer.BadParameter(message, param_hint=param_hint) from cause
101
101
 
102
102
 
103
- def cli_widget_key_error(param_hint: str) -> WidgetKeyErrorHandler:
104
- """
105
- Build a CLI error handler for missing form fields.
106
-
107
- Args:
108
- param_hint (str): CLI parameter associated with the field name.
109
-
110
- Returns:
111
- WidgetKeyErrorHandler: Handler that raises a Typer input error.
112
- """
113
-
114
- def _raise_cli_bad_parameter(message: str, cause: KeyError) -> NoReturn:
115
- cli_bad_parameter(message, param_hint=param_hint, cause=cause)
116
-
117
- return _raise_cli_bad_parameter
118
-
119
-
120
103
  def _validation_error_path(exc: ValidationError) -> str:
121
104
  """
122
105
  Builds a dotted JSON path for a validation error.
@@ -170,6 +153,31 @@ def load_json_file(data: Path, schema: dict, param_hint: str) -> Any:
170
153
  return input_data
171
154
 
172
155
 
156
+ def get_widget(wrapper: PdfWrapper, field: str, param_hint: str) -> Widget:
157
+ """
158
+ Look up a widget and report missing names as CLI input errors.
159
+
160
+ Args:
161
+ wrapper (PdfWrapper): PDF wrapper containing form widgets.
162
+ field (str): Form field name to look up.
163
+ param_hint (str): CLI parameter associated with the field name.
164
+
165
+ Returns:
166
+ Widget: The matching widget.
167
+
168
+ Raises:
169
+ typer.BadParameter: Raised when the widget name is not present.
170
+ """
171
+ try:
172
+ return wrapper.widgets[field]
173
+ except KeyError as exc:
174
+ cli_bad_parameter(
175
+ f"Form field '{field}' does not exist.",
176
+ param_hint=param_hint,
177
+ cause=exc,
178
+ )
179
+
180
+
173
181
  def handle_font_registration(
174
182
  obj: PdfWrapper, params: dict, registered_font: dict
175
183
  ) -> None:
@@ -11,7 +11,8 @@ import importlib
11
11
  import sys
12
12
 
13
13
  CLI_DEPENDENCIES = {"jsonschema", "typer"}
14
- CLI_INSTALL_HINT = "pip install 'PyPDFForm[cli]'"
14
+ CLI_INSTALL_HINT = 'pipx install "PyPDFForm[cli]"'
15
+ CLI_ENV_INSTALL_HINT = "pip install 'PyPDFForm[cli]'"
15
16
 
16
17
 
17
18
  def main() -> None:
@@ -28,7 +29,8 @@ def main() -> None:
28
29
  if exc.name in CLI_DEPENDENCIES:
29
30
  print(
30
31
  "PyPDFForm CLI dependencies are not installed. "
31
- f"Install them with: {CLI_INSTALL_HINT}",
32
+ f"Install them with: {CLI_INSTALL_HINT}. "
33
+ f"Inside an existing environment, use: {CLI_ENV_INSTALL_HINT}",
32
34
  file=sys.stderr,
33
35
  )
34
36
  raise SystemExit(1) from None
@@ -13,8 +13,7 @@ import json
13
13
  import typer
14
14
 
15
15
  from .. import PdfWrapper
16
- from ..shared.utils import get_widget
17
- from .common import FIELD_NAME, INPUT_PDF, cli_widget_key_error
16
+ from .common import FIELD_NAME, INPUT_PDF, get_widget
18
17
 
19
18
  inspect_cli = typer.Typer(
20
19
  context_settings={"help_option_names": ["--help", "-h"]}, no_args_is_help=True
@@ -55,9 +54,7 @@ def location(
55
54
  field: FIELD_NAME,
56
55
  ) -> None:
57
56
  """Print a form field's location and size as JSON."""
58
- f = get_widget(
59
- PdfWrapper(str(pdf), **ctx.obj), field, cli_widget_key_error("--field")
60
- )
57
+ f = get_widget(PdfWrapper(str(pdf), **ctx.obj), field, "--field")
61
58
 
62
59
  typer.echo(
63
60
  json.dumps(
@@ -1,4 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
+ # pylint: disable=R0801
2
3
  """
3
4
  JSON schemas for the PyPDFForm create CLI commands.
4
5
  """
@@ -1,4 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
+ # pylint: disable=R0801
2
3
  """
3
4
  JSON schemas for the PyPDFForm update CLI commands.
4
5
  """
@@ -17,11 +17,9 @@ import typer
17
17
 
18
18
  from .. import PdfWrapper
19
19
  from ..lib.constants import PdfVersion
20
- from ..shared.utils import get_widget
21
20
  from .common import (FIELD_NAME, INPUT_PDF, OPTIONAL_OUTPUT_PDF,
22
- cli_bad_parameter, cli_widget_key_error,
23
- handle_font_registration, json_file_option,
24
- load_json_file)
21
+ cli_bad_parameter, get_widget, handle_font_registration,
22
+ json_file_option, load_json_file)
25
23
  from .schemas.update import FIELD_SCHEMA, RENAME_SCHEMA
26
24
 
27
25
  update_cli = typer.Typer(
@@ -109,7 +107,7 @@ def bounds(
109
107
  ) -> None:
110
108
  """Update a form field's position and size."""
111
109
  obj = PdfWrapper(str(pdf), **ctx.obj)
112
- f = get_widget(obj, widget, cli_widget_key_error("--field"))
110
+ f = get_widget(obj, widget, "--field")
113
111
 
114
112
  f.x = x if x is not None else f.x
115
113
  f.y = y if y is not None else f.y
@@ -139,7 +137,7 @@ def rename(
139
137
  obj = PdfWrapper(str(pdf), **ctx.obj)
140
138
  for item in input_data:
141
139
  for k, v in item.items():
142
- widget = get_widget(obj, k, cli_widget_key_error("--file"))
140
+ widget = get_widget(obj, k, "--file")
143
141
  obj.update_widget_key(widget.name, v["new_key"], index=v.get("index", 0))
144
142
 
145
143
  obj.commit_widget_key_updates().write(output or pdf)
@@ -160,7 +158,7 @@ def field(
160
158
  obj = PdfWrapper(str(pdf), **ctx.obj)
161
159
  registered_font = {}
162
160
  for k, each in input_data.items():
163
- widget = get_widget(obj, k, cli_widget_key_error("--file"))
161
+ widget = get_widget(obj, k, "--file")
164
162
  handle_font_registration(obj, each, registered_font)
165
163
  for param, v in each.items():
166
164
  setattr(widget, param, v)
@@ -1,4 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
+ # pylint: disable=R0801
2
3
  """
3
4
  Contains the RawImage class, which represents an image that can be drawn
4
5
  directly onto a PDF page at a specified position and size.
@@ -1,4 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
+ # pylint: disable=R0801
2
3
  """
3
4
  Contains the RawRectangle class, which represents a rectangle that can be drawn
4
5
  directly onto a PDF page at specified coordinates and dimensions.
@@ -89,6 +89,7 @@ class DropdownField(Field):
89
89
  options: Optional[List[str | Tuple[str, str]]] = None
90
90
  width: Optional[float] = None
91
91
  height: Optional[float] = None
92
+ # pylint: disable=R0801
92
93
  font: Optional[str] = None
93
94
  font_size: Optional[float] = None
94
95
  font_color: Optional[Tuple[float, ...]] = None
@@ -121,6 +121,7 @@ class SignatureWidget:
121
121
  annot_type_to_annot[key] = annot.get_object()
122
122
 
123
123
  for i, p in enumerate(input_pdf.pages):
124
+ # pylint: disable=R0801
124
125
  watermark = BytesIO()
125
126
  canvas = Canvas(
126
127
  watermark,
@@ -1,13 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPDFForm
3
- Version: 5.0.0
4
- Summary: The Python library for PDF forms.
3
+ Version: 5.1.0
4
+ Summary: The Python library & CLI for PDF forms.
5
5
  Author: Jinge Li
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/chinapandaman/PyPDFForm
8
8
  Project-URL: Documentation, https://chinapandaman.github.io/PyPDFForm/
9
9
  Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Environment :: Console
10
11
  Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
11
13
  Classifier: Programming Language :: Python :: 3
12
14
  Classifier: Programming Language :: Python :: 3 :: Only
13
15
  Classifier: Programming Language :: Python :: 3.10
@@ -15,24 +17,24 @@ Classifier: Programming Language :: Python :: 3.11
15
17
  Classifier: Programming Language :: Python :: 3.12
16
18
  Classifier: Programming Language :: Python :: 3.13
17
19
  Classifier: Programming Language :: Python :: 3.14
18
- Classifier: Operating System :: OS Independent
20
+ Classifier: Topic :: File Formats
19
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Text Processing
23
+ Classifier: Topic :: Utilities
20
24
  Requires-Python: >=3.10
21
25
  Description-Content-Type: text/markdown
22
26
  License-File: LICENSE
23
27
  Requires-Dist: cryptography<49.0.0,>=48.0.0
24
- Requires-Dist: fonttools<5.0.0,>=4.62.1
28
+ Requires-Dist: fonttools<5.0.0,>=4.63.0
25
29
  Requires-Dist: pikepdf<11.0.0,>=10.5.1
26
30
  Requires-Dist: pillow<13.0.0,>=12.2.0
27
31
  Requires-Dist: pypdf<7.0.0,>=6.11.0
28
- Requires-Dist: reportlab<5.0.0,>=4.5.0
32
+ Requires-Dist: reportlab<5.0.0,>=4.5.1
29
33
  Provides-Extra: cli
30
34
  Requires-Dist: typer<1.0.0,>=0.25.1; extra == "cli"
31
35
  Requires-Dist: jsonschema<5.0.0,>=4.26.0; extra == "cli"
32
- Provides-Extra: web-api
33
- Requires-Dist: fastapi[standard]<1.0.0,>=0.136.1; extra == "web-api"
34
36
  Provides-Extra: dev
35
- Requires-Dist: black<27.0.0,>=26.3.1; extra == "dev"
37
+ Requires-Dist: black<27.0.0,>=26.5.1; extra == "dev"
36
38
  Requires-Dist: coverage<8.0.0,>=7.14.0; extra == "dev"
37
39
  Requires-Dist: isort<9.0.0,>=8.0.1; extra == "dev"
38
40
  Requires-Dist: mike<3.0.0,>=2.2.0; extra == "dev"
@@ -42,13 +44,13 @@ Requires-Dist: pudb<2026.0.0,>=2025.1.5; extra == "dev"
42
44
  Requires-Dist: pylint<5.0.0,>=4.0.5; extra == "dev"
43
45
  Requires-Dist: pyright<2.0.0,>=1.1.409; extra == "dev"
44
46
  Requires-Dist: pytest<10.0.0,>=9.0.3; extra == "dev"
45
- Requires-Dist: requests<3.0.0,>=2.34.0; extra == "dev"
46
- Requires-Dist: ruff<1.0.0,>=0.15.12; extra == "dev"
47
+ Requires-Dist: requests<3.0.0,>=2.34.2; extra == "dev"
48
+ Requires-Dist: ruff<1.0.0,>=0.15.13; extra == "dev"
47
49
  Dynamic: license-file
48
50
 
49
51
  <p align="center"><img src="https://github.com/chinapandaman/PyPDFForm/raw/master/docs/img/logo.png"></p>
50
52
  <p align="center">
51
- <em>PDF Form Automation Simplified Create, Merge, Style, and Fill Forms Programmatically.</em>
53
+ <em>PDF Form Automation Simplified - Create, Inspect, Style, and Fill Forms in Python or from the Command Line.</em>
52
54
  </p>
53
55
  <p align="center">
54
56
  <a href="https://pypi.org/project/PyPDFForm/"><img src="https://img.shields.io/pypi/v/pypdfform?label=version&color=magenta"></a>
@@ -61,26 +63,36 @@ Dynamic: license-file
61
63
 
62
64
  ## Introduction
63
65
 
64
- PyPDFForm is a Python library for PDF form processing. It contains the essential functionalities needed to interact with PDF forms:
66
+ PyPDFForm is a Python library and command line tool for working with PDF forms. It provides Python APIs and CLI commands for creating, inspecting, updating, and filling forms, plus common PDF utilities.
67
+
68
+ With PyPDFForm, you can:
65
69
 
66
- * Inspect what data a PDF form needs to be filled with.
67
- * Fill a PDF form by simply creating a Python dictionary.
68
- * Create form fields on a PDF.
70
+ * Create PDF forms, form fields, and raw elements.
71
+ * Inspect form fields, metadata, and values.
72
+ * Update field styling, behavior, and scripts.
73
+ * Fill PDF forms.
74
+ * Extract pages and merge PDFs.
69
75
 
70
- It also supports other common utilities such as extracting pages and merging multiple PDFs together.
76
+ The goal is to make PDF form work straightforward, whether you are handling one document or building a larger workflow.
71
77
 
72
78
  ## Installing
73
79
 
74
- Install using [pip](https://pypi.org/project/PyPDFForm/):
80
+ To use PyPDFForm as a Python library, install the base package with [pip](https://pypi.org/project/PyPDFForm/):
75
81
 
76
- ```shell script
82
+ ```shell
77
83
  pip install PyPDFForm
78
84
  ```
79
85
 
86
+ To use the CLI, install PyPDFForm with the `cli` extra using [pipx](https://pipx.pypa.io/stable/):
87
+
88
+ ```shell
89
+ pipx install "PyPDFForm[cli]"
90
+ ```
91
+
80
92
  ## Quick Example
81
93
  ![Check out the GitHub repository for a live demo if you can't see it here.](https://github.com/chinapandaman/PyPDFForm/raw/master/docs/img/demo.gif)
82
94
 
83
- A sample PDF form can be found [here](https://chinapandaman.github.io/PyPDFForm/latest/pdfs/sample_template.pdf). Download it and try:
95
+ The GIF above shows the CLI filling a PDF form. To try the same workflow with the Python library, download the [sample PDF form](https://chinapandaman.github.io/PyPDFForm/latest/pdfs/sample_template.pdf) and run:
84
96
 
85
97
  ```python
86
98
  from PyPDFForm import PdfWrapper
@@ -99,8 +111,7 @@ filled = PdfWrapper("sample_template.pdf", need_appearances=True).fill(
99
111
  filled.write("output.pdf")
100
112
  ```
101
113
 
102
- After running the above code snippet you can find `output.pdf` at the location you specified,
103
- and it should look like [this](https://chinapandaman.github.io/PyPDFForm/latest/pdfs/sample_filled.pdf).
114
+ After running this snippet, `output.pdf` will be written to the location you specified and should look like [this](https://chinapandaman.github.io/PyPDFForm/latest/pdfs/sample_filled.pdf).
104
115
 
105
116
  ## Documentation
106
117
 
@@ -8,12 +8,6 @@ PyPDFForm.egg-info/dependency_links.txt
8
8
  PyPDFForm.egg-info/entry_points.txt
9
9
  PyPDFForm.egg-info/requires.txt
10
10
  PyPDFForm.egg-info/top_level.txt
11
- PyPDFForm/api/__init__.py
12
- PyPDFForm/api/common.py
13
- PyPDFForm/api/create.py
14
- PyPDFForm/api/inspect.py
15
- PyPDFForm/api/root.py
16
- PyPDFForm/api/update.py
17
11
  PyPDFForm/cli/__init__.py
18
12
  PyPDFForm/cli/common.py
19
13
  PyPDFForm/cli/create.py
@@ -72,8 +66,6 @@ PyPDFForm/lib/widgets/image.py
72
66
  PyPDFForm/lib/widgets/radio.py
73
67
  PyPDFForm/lib/widgets/signature.py
74
68
  PyPDFForm/lib/widgets/text.py
75
- PyPDFForm/shared/__init__.py
76
- PyPDFForm/shared/utils.py
77
69
  tests/test_bulk_create_fields.py
78
70
  tests/test_create_widget.py
79
71
  tests/test_draw_elements.py
@@ -1,16 +1,16 @@
1
1
  cryptography<49.0.0,>=48.0.0
2
- fonttools<5.0.0,>=4.62.1
2
+ fonttools<5.0.0,>=4.63.0
3
3
  pikepdf<11.0.0,>=10.5.1
4
4
  pillow<13.0.0,>=12.2.0
5
5
  pypdf<7.0.0,>=6.11.0
6
- reportlab<5.0.0,>=4.5.0
6
+ reportlab<5.0.0,>=4.5.1
7
7
 
8
8
  [cli]
9
9
  typer<1.0.0,>=0.25.1
10
10
  jsonschema<5.0.0,>=4.26.0
11
11
 
12
12
  [dev]
13
- black<27.0.0,>=26.3.1
13
+ black<27.0.0,>=26.5.1
14
14
  coverage<8.0.0,>=7.14.0
15
15
  isort<9.0.0,>=8.0.1
16
16
  mike<3.0.0,>=2.2.0
@@ -20,8 +20,5 @@ pudb<2026.0.0,>=2025.1.5
20
20
  pylint<5.0.0,>=4.0.5
21
21
  pyright<2.0.0,>=1.1.409
22
22
  pytest<10.0.0,>=9.0.3
23
- requests<3.0.0,>=2.34.0
24
- ruff<1.0.0,>=0.15.12
25
-
26
- [web_api]
27
- fastapi[standard]<1.0.0,>=0.136.1
23
+ requests<3.0.0,>=2.34.2
24
+ ruff<1.0.0,>=0.15.13
@@ -1,6 +1,6 @@
1
1
  <p align="center"><img src="https://github.com/chinapandaman/PyPDFForm/raw/master/docs/img/logo.png"></p>
2
2
  <p align="center">
3
- <em>PDF Form Automation Simplified Create, Merge, Style, and Fill Forms Programmatically.</em>
3
+ <em>PDF Form Automation Simplified - Create, Inspect, Style, and Fill Forms in Python or from the Command Line.</em>
4
4
  </p>
5
5
  <p align="center">
6
6
  <a href="https://pypi.org/project/PyPDFForm/"><img src="https://img.shields.io/pypi/v/pypdfform?label=version&color=magenta"></a>
@@ -13,26 +13,36 @@
13
13
 
14
14
  ## Introduction
15
15
 
16
- PyPDFForm is a Python library for PDF form processing. It contains the essential functionalities needed to interact with PDF forms:
16
+ PyPDFForm is a Python library and command line tool for working with PDF forms. It provides Python APIs and CLI commands for creating, inspecting, updating, and filling forms, plus common PDF utilities.
17
17
 
18
- * Inspect what data a PDF form needs to be filled with.
19
- * Fill a PDF form by simply creating a Python dictionary.
20
- * Create form fields on a PDF.
18
+ With PyPDFForm, you can:
21
19
 
22
- It also supports other common utilities such as extracting pages and merging multiple PDFs together.
20
+ * Create PDF forms, form fields, and raw elements.
21
+ * Inspect form fields, metadata, and values.
22
+ * Update field styling, behavior, and scripts.
23
+ * Fill PDF forms.
24
+ * Extract pages and merge PDFs.
25
+
26
+ The goal is to make PDF form work straightforward, whether you are handling one document or building a larger workflow.
23
27
 
24
28
  ## Installing
25
29
 
26
- Install using [pip](https://pypi.org/project/PyPDFForm/):
30
+ To use PyPDFForm as a Python library, install the base package with [pip](https://pypi.org/project/PyPDFForm/):
27
31
 
28
- ```shell script
32
+ ```shell
29
33
  pip install PyPDFForm
30
34
  ```
31
35
 
36
+ To use the CLI, install PyPDFForm with the `cli` extra using [pipx](https://pipx.pypa.io/stable/):
37
+
38
+ ```shell
39
+ pipx install "PyPDFForm[cli]"
40
+ ```
41
+
32
42
  ## Quick Example
33
43
  ![Check out the GitHub repository for a live demo if you can't see it here.](https://github.com/chinapandaman/PyPDFForm/raw/master/docs/img/demo.gif)
34
44
 
35
- A sample PDF form can be found [here](https://chinapandaman.github.io/PyPDFForm/latest/pdfs/sample_template.pdf). Download it and try:
45
+ The GIF above shows the CLI filling a PDF form. To try the same workflow with the Python library, download the [sample PDF form](https://chinapandaman.github.io/PyPDFForm/latest/pdfs/sample_template.pdf) and run:
36
46
 
37
47
  ```python
38
48
  from PyPDFForm import PdfWrapper
@@ -51,8 +61,7 @@ filled = PdfWrapper("sample_template.pdf", need_appearances=True).fill(
51
61
  filled.write("output.pdf")
52
62
  ```
53
63
 
54
- After running the above code snippet you can find `output.pdf` at the location you specified,
55
- and it should look like [this](https://chinapandaman.github.io/PyPDFForm/latest/pdfs/sample_filled.pdf).
64
+ After running this snippet, `output.pdf` will be written to the location you specified and should look like [this](https://chinapandaman.github.io/PyPDFForm/latest/pdfs/sample_filled.pdf).
56
65
 
57
66
  ## Documentation
58
67