PyPDFForm 4.8.1__tar.gz → 4.8.3__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 (86) hide show
  1. {pypdfform-4.8.1 → pypdfform-4.8.3}/PKG-INFO +7 -7
  2. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/__init__.py +1 -1
  3. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/cli/common.py +66 -5
  4. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/cli/create.py +40 -4
  5. pypdfform-4.8.3/PyPDFForm/cli/entry.py +37 -0
  6. pypdfform-4.8.1/PyPDFForm/cli/__init__.py → pypdfform-4.8.3/PyPDFForm/cli/root.py +28 -10
  7. pypdfform-4.8.3/PyPDFForm/cli/schemas/create.py +525 -0
  8. pypdfform-4.8.3/PyPDFForm/cli/schemas/update.py +74 -0
  9. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/cli/update.py +19 -9
  10. pypdfform-4.8.3/PyPDFForm/lib/__init__.py +0 -0
  11. pypdfform-4.8.3/PyPDFForm/lib/assets/__init__.py +0 -0
  12. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/middleware/dropdown.py +8 -3
  13. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/wrapper.py +1 -0
  14. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm.egg-info/PKG-INFO +7 -7
  15. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm.egg-info/SOURCES.txt +5 -0
  16. pypdfform-4.8.3/PyPDFForm.egg-info/entry_points.txt +2 -0
  17. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm.egg-info/requires.txt +6 -6
  18. {pypdfform-4.8.1 → pypdfform-4.8.3}/pyproject.toml +6 -7
  19. {pypdfform-4.8.1 → pypdfform-4.8.3}/tests/test_dropdown.py +3 -1
  20. pypdfform-4.8.1/PyPDFForm.egg-info/entry_points.txt +0 -2
  21. {pypdfform-4.8.1 → pypdfform-4.8.3}/LICENSE +0 -0
  22. {pypdfform-4.8.1/PyPDFForm/lib → pypdfform-4.8.3/PyPDFForm/cli}/__init__.py +0 -0
  23. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/cli/inspect.py +0 -0
  24. {pypdfform-4.8.1/PyPDFForm/lib/assets → pypdfform-4.8.3/PyPDFForm/cli/schemas}/__init__.py +0 -0
  25. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/adapter.py +0 -0
  26. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/annotations/__init__.py +0 -0
  27. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/annotations/base.py +0 -0
  28. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/annotations/link.py +0 -0
  29. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/annotations/stamp.py +0 -0
  30. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/annotations/text.py +0 -0
  31. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/annotations/text_markup.py +0 -0
  32. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/assets/bedrock.py +0 -0
  33. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/assets/blank.py +0 -0
  34. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/constants.py +0 -0
  35. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/coordinate.py +0 -0
  36. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/deprecation.py +0 -0
  37. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/egress.py +0 -0
  38. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/filler.py +0 -0
  39. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/font.py +0 -0
  40. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/hooks.py +0 -0
  41. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/image.py +0 -0
  42. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/middleware/__init__.py +0 -0
  43. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/middleware/base.py +0 -0
  44. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/middleware/checkbox.py +0 -0
  45. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/middleware/image.py +0 -0
  46. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/middleware/radio.py +0 -0
  47. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/middleware/signature.py +0 -0
  48. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/middleware/text.py +0 -0
  49. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/patterns.py +0 -0
  50. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/raw/__init__.py +0 -0
  51. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/raw/circle.py +0 -0
  52. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/raw/ellipse.py +0 -0
  53. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/raw/image.py +0 -0
  54. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/raw/line.py +0 -0
  55. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/raw/rect.py +0 -0
  56. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/raw/text.py +0 -0
  57. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/template.py +0 -0
  58. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/types.py +0 -0
  59. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/utils.py +0 -0
  60. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/watermark.py +0 -0
  61. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/widgets/__init__.py +0 -0
  62. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/widgets/base.py +0 -0
  63. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/widgets/checkbox.py +0 -0
  64. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/widgets/dropdown.py +0 -0
  65. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/widgets/image.py +0 -0
  66. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/widgets/radio.py +0 -0
  67. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/widgets/signature.py +0 -0
  68. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm/lib/widgets/text.py +0 -0
  69. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm.egg-info/dependency_links.txt +0 -0
  70. {pypdfform-4.8.1 → pypdfform-4.8.3}/PyPDFForm.egg-info/top_level.txt +0 -0
  71. {pypdfform-4.8.1 → pypdfform-4.8.3}/README.md +0 -0
  72. {pypdfform-4.8.1 → pypdfform-4.8.3}/setup.cfg +0 -0
  73. {pypdfform-4.8.1 → pypdfform-4.8.3}/tests/test_bulk_create_fields.py +0 -0
  74. {pypdfform-4.8.1 → pypdfform-4.8.3}/tests/test_create_widget.py +0 -0
  75. {pypdfform-4.8.1 → pypdfform-4.8.3}/tests/test_draw_elements.py +0 -0
  76. {pypdfform-4.8.1 → pypdfform-4.8.3}/tests/test_extract_middleware_attributes.py +0 -0
  77. {pypdfform-4.8.1 → pypdfform-4.8.3}/tests/test_fill_max_length_text_field.py +0 -0
  78. {pypdfform-4.8.1 → pypdfform-4.8.3}/tests/test_font_widths.py +0 -0
  79. {pypdfform-4.8.1 → pypdfform-4.8.3}/tests/test_functional.py +0 -0
  80. {pypdfform-4.8.1 → pypdfform-4.8.3}/tests/test_generate_appearance_streams.py +0 -0
  81. {pypdfform-4.8.1 → pypdfform-4.8.3}/tests/test_js.py +0 -0
  82. {pypdfform-4.8.1 → pypdfform-4.8.3}/tests/test_need_appearances.py +0 -0
  83. {pypdfform-4.8.1 → pypdfform-4.8.3}/tests/test_paragraph.py +0 -0
  84. {pypdfform-4.8.1 → pypdfform-4.8.3}/tests/test_signature.py +0 -0
  85. {pypdfform-4.8.1 → pypdfform-4.8.3}/tests/test_use_full_widget_name.py +0 -0
  86. {pypdfform-4.8.1 → pypdfform-4.8.3}/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.8.1
3
+ Version: 4.8.3
4
4
  Summary: The Python library for PDF forms.
5
5
  Author: Jinge Li
6
6
  License-Expression: MIT
@@ -20,19 +20,19 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
20
  Requires-Python: >=3.10
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
- Requires-Dist: cryptography<47.0.0,>=46.0.3
23
+ Requires-Dist: cryptography<48.0.0,>=47.0.0
24
24
  Requires-Dist: fonttools<5.0.0,>=4.62.1
25
- Requires-Dist: pikepdf<11.0.0,>=10.5.0
25
+ Requires-Dist: pikepdf<11.0.0,>=10.5.1
26
26
  Requires-Dist: pillow<13.0.0,>=12.2.0
27
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
- Requires-Dist: typer<1.0.0,>=0.24.1; extra == "cli"
30
+ Requires-Dist: typer<1.0.0,>=0.25.0; extra == "cli"
31
+ Requires-Dist: jsonschema<5.0.0,>=4.26.0; extra == "cli"
31
32
  Provides-Extra: dev
32
33
  Requires-Dist: black<27.0.0,>=26.3.1; extra == "dev"
33
- Requires-Dist: coverage<8.0.0,>=7.12.0; extra == "dev"
34
+ Requires-Dist: coverage<8.0.0,>=7.13.5; extra == "dev"
34
35
  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.6; extra == "dev"
@@ -41,7 +41,7 @@ Requires-Dist: pylint<5.0.0,>=4.0.5; extra == "dev"
41
41
  Requires-Dist: pyright<2.0.0,>=1.1.407; extra == "dev"
42
42
  Requires-Dist: pytest<10.0.0,>=9.0.3; extra == "dev"
43
43
  Requires-Dist: requests<3.0.0,>=2.33.1; extra == "dev"
44
- Requires-Dist: ruff<1.0.0,>=0.14.6; extra == "dev"
44
+ Requires-Dist: ruff<1.0.0,>=0.15.12; extra == "dev"
45
45
  Dynamic: license-file
46
46
 
47
47
  <p align="center"><img src="https://github.com/chinapandaman/PyPDFForm/raw/master/docs/img/logo.png"></p>
@@ -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.8.1"
25
+ __version__ = "4.8.3"
26
26
 
27
27
  from .lib.annotations import Annotations
28
28
  from .lib.assets.blank import BlankPage
@@ -9,9 +9,10 @@ into the objects expected by `PdfWrapper` methods.
9
9
 
10
10
  import json
11
11
  from pathlib import Path
12
- from typing import Annotated, NoReturn
12
+ from typing import Annotated, Any, NoReturn
13
13
 
14
14
  import typer
15
+ from jsonschema import ValidationError, validate
15
16
 
16
17
  from .. import PdfWrapper
17
18
  from ..lib.middleware.base import Widget
@@ -79,7 +80,7 @@ def json_file_option(help_text: str):
79
80
  def cli_bad_parameter(
80
81
  message: str,
81
82
  param_hint: str,
82
- cause: BaseException,
83
+ cause: BaseException | None = None,
83
84
  ) -> NoReturn:
84
85
  """
85
86
  Raises a Typer input error with a stable CLI message.
@@ -87,14 +88,71 @@ def cli_bad_parameter(
87
88
  Args:
88
89
  message (str): Error message to display to the CLI user.
89
90
  param_hint (str): CLI parameter associated with the error.
90
- cause (BaseException): Original exception that caused the CLI error.
91
+ cause (BaseException, optional): Original exception that caused the CLI
92
+ error. Defaults to None.
91
93
 
92
94
  Raises:
93
95
  typer.BadParameter: Raised with the provided message and parameter hint.
94
96
  """
97
+ if cause is None:
98
+ raise typer.BadParameter(message, param_hint=param_hint)
99
+
95
100
  raise typer.BadParameter(message, param_hint=param_hint) from cause
96
101
 
97
102
 
103
+ def _validation_error_path(exc: ValidationError) -> str:
104
+ """
105
+ Builds a dotted JSON path for a validation error.
106
+
107
+ Args:
108
+ exc (ValidationError): The JSON schema validation error.
109
+
110
+ Returns:
111
+ str: Dotted path for the failing instance location.
112
+ """
113
+ return ".".join(str(each) for each in exc.absolute_path)
114
+
115
+
116
+ def load_json_file(data: Path, schema: dict, param_hint: str) -> Any:
117
+ """
118
+ Loads a JSON CLI input file and validates it against a schema.
119
+
120
+ Args:
121
+ data (Path): JSON file path.
122
+ schema (dict): JSON schema to validate against.
123
+ param_hint (str): CLI parameter associated with the JSON file.
124
+
125
+ Returns:
126
+ Any: Parsed and validated JSON input.
127
+
128
+ Raises:
129
+ typer.BadParameter: Raised when the file cannot be loaded or validation
130
+ fails.
131
+ """
132
+ try:
133
+ with open(data, "r", encoding="utf-8") as f:
134
+ input_data = json.load(f)
135
+ except (OSError, json.JSONDecodeError) as exc:
136
+ cli_bad_parameter(
137
+ f"Invalid JSON file: {exc}",
138
+ param_hint=param_hint,
139
+ cause=exc,
140
+ )
141
+
142
+ try:
143
+ validate(instance=input_data, schema=schema)
144
+ except ValidationError as exc:
145
+ error_path = _validation_error_path(exc)
146
+ location = f" at {error_path}" if error_path else ""
147
+ cli_bad_parameter(
148
+ f"Invalid JSON file{location}: {exc.message}",
149
+ param_hint=param_hint,
150
+ cause=exc,
151
+ )
152
+
153
+ return input_data
154
+
155
+
98
156
  def get_widget(wrapper: PdfWrapper, field: str, param_hint: str) -> Widget:
99
157
  """
100
158
  Look up a widget and report missing names as CLI input errors.
@@ -150,8 +208,10 @@ def create_elements_from_file(
150
208
  pdf: Path,
151
209
  data: Path,
152
210
  element_map: dict,
211
+ schema: dict,
153
212
  method_name: str,
154
213
  ctx: typer.Context,
214
+ param_hint: str,
155
215
  output: Path | None = None,
156
216
  ) -> None:
157
217
  """
@@ -169,16 +229,17 @@ def create_elements_from_file(
169
229
  definitions.
170
230
  element_map (dict): Mapping from JSON group names to element classes or
171
231
  callables used to construct each object.
232
+ schema (dict): JSON schema used to validate the grouped definitions.
172
233
  method_name (str): Name of the `PdfWrapper` method that accepts the
173
234
  constructed elements, such as `bulk_create_fields`, `draw`, or
174
235
  `annotate`.
175
236
  ctx (typer.Context): Typer context containing global wrapper options in
176
237
  `ctx.obj`.
238
+ param_hint (str): CLI parameter associated with the JSON file.
177
239
  output (Path, optional): Path where the modified PDF should be saved. If
178
240
  omitted, the input PDF is overwritten. Defaults to None.
179
241
  """
180
- with open(data, "r", encoding="utf-8") as f:
181
- input_data = json.load(f)
242
+ input_data = load_json_file(data, schema, param_hint)
182
243
 
183
244
  obj = PdfWrapper(str(pdf), **ctx.obj)
184
245
  ungrouped_input = []
@@ -17,7 +17,9 @@ import typer
17
17
  from .. import (Annotations, BlankPage, Fields, PdfArray, PdfWrapper,
18
18
  RawElements)
19
19
  from .common import (INPUT_PDF, OPTIONAL_OUTPUT_PDF, REQUIRED_OUTPUT_PDF,
20
- create_elements_from_file, json_file_option)
20
+ cli_bad_parameter, create_elements_from_file,
21
+ json_file_option)
22
+ from .schemas.create import ANNOTATION_SCHEMA, FIELD_SCHEMA, RAW_SCHEMA
21
23
 
22
24
  create_cli = typer.Typer(
23
25
  context_settings={"help_option_names": ["--help", "-h"]}, no_args_is_help=True
@@ -93,6 +95,13 @@ def extract(
93
95
  ] = None,
94
96
  ) -> None:
95
97
  """Extract pages from an existing PDF."""
98
+ if start is not None and end is not None and start > end:
99
+ message = "End page must be greater than or equal to start page."
100
+ cli_bad_parameter(
101
+ message,
102
+ param_hint="--end",
103
+ )
104
+
96
105
  PdfWrapper(str(pdf), **ctx.obj).pages[slice((start or 1) - 1, end)].write(output)
97
106
 
98
107
 
@@ -132,7 +141,16 @@ def field(
132
141
  "image": Fields.ImageField,
133
142
  "signature": Fields.SignatureField,
134
143
  }
135
- create_elements_from_file(pdf, data, field_map, "bulk_create_fields", ctx, output)
144
+ create_elements_from_file(
145
+ pdf=pdf,
146
+ data=data,
147
+ element_map=field_map,
148
+ schema=FIELD_SCHEMA,
149
+ method_name="bulk_create_fields",
150
+ ctx=ctx,
151
+ param_hint="--file",
152
+ output=output,
153
+ )
136
154
 
137
155
 
138
156
  @create_cli.command(no_args_is_help=True)
@@ -151,7 +169,16 @@ def raw(
151
169
  "circle": RawElements.RawCircle,
152
170
  "ellipse": RawElements.RawEllipse,
153
171
  }
154
- create_elements_from_file(pdf, data, raw_element_map, "draw", ctx, output)
172
+ create_elements_from_file(
173
+ pdf=pdf,
174
+ data=data,
175
+ element_map=raw_element_map,
176
+ schema=RAW_SCHEMA,
177
+ method_name="draw",
178
+ ctx=ctx,
179
+ param_hint="--file",
180
+ output=output,
181
+ )
155
182
 
156
183
 
157
184
  @create_cli.command(no_args_is_help=True)
@@ -171,7 +198,16 @@ def annotation(
171
198
  "strikeout": Annotations.StrikeOutAnnotation,
172
199
  "stamp": Annotations.RubberStampAnnotation,
173
200
  }
174
- create_elements_from_file(pdf, data, annotation_map, "annotate", ctx, output)
201
+ create_elements_from_file(
202
+ pdf=pdf,
203
+ data=data,
204
+ element_map=annotation_map,
205
+ schema=ANNOTATION_SCHEMA,
206
+ method_name="annotate",
207
+ ctx=ctx,
208
+ param_hint="--file",
209
+ output=output,
210
+ )
175
211
 
176
212
 
177
213
  @create_cli.command(no_args_is_help=True)
@@ -0,0 +1,37 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Console script entry point for the optional PyPDFForm CLI.
4
+
5
+ The CLI implementation depends on the optional ``cli`` extra. This lightweight
6
+ wrapper lets the ``pypdfform`` command fail with installation guidance instead
7
+ of an import traceback when those optional dependencies are absent.
8
+ """
9
+
10
+ import importlib
11
+ import sys
12
+
13
+ CLI_DEPENDENCIES = {"jsonschema", "typer"}
14
+ CLI_INSTALL_HINT = "pip install 'PyPDFForm[cli]'"
15
+
16
+
17
+ def main() -> None:
18
+ """
19
+ Run the PyPDFForm CLI.
20
+
21
+ Raises:
22
+ SystemExit: Raised with exit code 1 when optional CLI dependencies are
23
+ missing.
24
+ """
25
+ try:
26
+ cli_module = importlib.import_module("PyPDFForm.cli.root")
27
+ except ModuleNotFoundError as exc:
28
+ if exc.name in CLI_DEPENDENCIES:
29
+ print(
30
+ "PyPDFForm CLI dependencies are not installed. "
31
+ f"Install them with: {CLI_INSTALL_HINT}",
32
+ file=sys.stderr,
33
+ )
34
+ raise SystemExit(1) from None
35
+ raise
36
+
37
+ getattr(cli_module, "cli_app")()
@@ -14,14 +14,14 @@ Commands:
14
14
  - `update`: Modify PDF metadata, field names, properties, geometry, and scripts.
15
15
  """
16
16
 
17
- import json
18
17
  from pathlib import Path
19
18
  from typing import Annotated
20
19
 
21
20
  import typer
22
21
 
23
22
  from .. import PdfWrapper, Widgets, __version__
24
- from .common import INPUT_PDF, OPTIONAL_OUTPUT_PDF, json_file_option
23
+ from .common import (INPUT_PDF, OPTIONAL_OUTPUT_PDF, json_file_option,
24
+ load_json_file)
25
25
  from .create import create_cli
26
26
  from .inspect import inspect_cli
27
27
  from .update import update_cli
@@ -132,18 +132,36 @@ def fill(
132
132
  ] = None,
133
133
  ) -> None:
134
134
  """Fill a PDF form with JSON data."""
135
- with open(data, "r", encoding="utf-8") as f:
136
- input_data = json.load(f)
137
-
138
135
  obj = PdfWrapper(str(pdf), **ctx.obj)
136
+
137
+ schema = obj.schema
138
+ for key, widget in obj.widgets.items():
139
+ if isinstance(widget, (Widgets.Image, Widgets.Signature)):
140
+ schema["properties"][key] = {
141
+ "anyOf": [
142
+ schema["properties"][key],
143
+ {
144
+ "type": "object",
145
+ "properties": {
146
+ "path": {"type": "string"},
147
+ "preserve_aspect_ratio": {"type": "boolean"},
148
+ },
149
+ "required": ["path"],
150
+ "additionalProperties": False,
151
+ },
152
+ ]
153
+ }
154
+
155
+ input_data = load_json_file(data, schema, "--file")
139
156
  for k, each in obj.widgets.items():
140
- if k in input_data and isinstance(each, (Widgets.Image, Widgets.Signature)):
141
- each.preserve_aspect_ratio = input_data.get(k, {}).get(
157
+ if (
158
+ k in input_data
159
+ and isinstance(each, (Widgets.Image, Widgets.Signature))
160
+ and isinstance(input_data[k], dict)
161
+ ):
162
+ each.preserve_aspect_ratio = input_data[k].get(
142
163
  "preserve_aspect_ratio", each.preserve_aspect_ratio
143
164
  )
144
165
  input_data[k] = input_data[k]["path"]
145
166
 
146
167
  obj.fill(input_data, flatten=flatten).write(output or pdf)
147
-
148
-
149
- __all__ = ["cli_app"]