PyPDFForm 4.8.1__tar.gz → 4.8.2__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.
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PKG-INFO +2 -2
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/__init__.py +1 -1
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/cli/__init__.py +28 -7
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/cli/common.py +62 -5
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/cli/create.py +31 -3
- pypdfform-4.8.2/PyPDFForm/cli/schemas/create.py +525 -0
- pypdfform-4.8.2/PyPDFForm/cli/schemas/update.py +74 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/cli/update.py +11 -8
- pypdfform-4.8.2/PyPDFForm/lib/assets/__init__.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/middleware/dropdown.py +8 -3
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/wrapper.py +1 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm.egg-info/PKG-INFO +2 -2
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm.egg-info/SOURCES.txt +3 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/pyproject.toml +1 -2
- {pypdfform-4.8.1 → pypdfform-4.8.2}/tests/test_dropdown.py +3 -1
- {pypdfform-4.8.1 → pypdfform-4.8.2}/LICENSE +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/cli/inspect.py +0 -0
- {pypdfform-4.8.1/PyPDFForm/lib → pypdfform-4.8.2/PyPDFForm/cli/schemas}/__init__.py +0 -0
- {pypdfform-4.8.1/PyPDFForm/lib/assets → pypdfform-4.8.2/PyPDFForm/lib}/__init__.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/adapter.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/annotations/__init__.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/annotations/base.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/annotations/link.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/annotations/stamp.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/annotations/text.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/annotations/text_markup.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/assets/bedrock.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/assets/blank.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/constants.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/coordinate.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/deprecation.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/egress.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/filler.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/font.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/hooks.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/image.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/middleware/__init__.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/middleware/base.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/middleware/checkbox.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/middleware/image.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/middleware/radio.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/middleware/signature.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/middleware/text.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/patterns.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/raw/__init__.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/raw/circle.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/raw/ellipse.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/raw/image.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/raw/line.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/raw/rect.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/raw/text.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/template.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/types.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/utils.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/watermark.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/widgets/__init__.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/widgets/base.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/widgets/checkbox.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/widgets/dropdown.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/widgets/image.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/widgets/radio.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/widgets/signature.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm/lib/widgets/text.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm.egg-info/dependency_links.txt +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm.egg-info/entry_points.txt +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm.egg-info/requires.txt +1 -1
- {pypdfform-4.8.1 → pypdfform-4.8.2}/PyPDFForm.egg-info/top_level.txt +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/README.md +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/setup.cfg +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/tests/test_bulk_create_fields.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/tests/test_create_widget.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/tests/test_draw_elements.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/tests/test_extract_middleware_attributes.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/tests/test_fill_max_length_text_field.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/tests/test_font_widths.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/tests/test_functional.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/tests/test_generate_appearance_streams.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/tests/test_js.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/tests/test_need_appearances.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/tests/test_paragraph.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/tests/test_signature.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/tests/test_use_full_widget_name.py +0 -0
- {pypdfform-4.8.1 → pypdfform-4.8.2}/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.
|
|
3
|
+
Version: 4.8.2
|
|
4
4
|
Summary: The Python library for PDF forms.
|
|
5
5
|
Author: Jinge Li
|
|
6
6
|
License-Expression: MIT
|
|
@@ -28,11 +28,11 @@ 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
|
+
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
34
|
Requires-Dist: coverage<8.0.0,>=7.12.0; 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"
|
|
@@ -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,13 +132,34 @@ 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
|
|
141
|
-
|
|
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"]
|
|
@@ -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
|
|
@@ -76,7 +77,7 @@ def json_file_option(help_text: str):
|
|
|
76
77
|
)
|
|
77
78
|
|
|
78
79
|
|
|
79
|
-
def
|
|
80
|
+
def _cli_bad_parameter(
|
|
80
81
|
message: str,
|
|
81
82
|
param_hint: str,
|
|
82
83
|
cause: BaseException,
|
|
@@ -95,6 +96,59 @@ def cli_bad_parameter(
|
|
|
95
96
|
raise typer.BadParameter(message, param_hint=param_hint) from cause
|
|
96
97
|
|
|
97
98
|
|
|
99
|
+
def _validation_error_path(exc: ValidationError) -> str:
|
|
100
|
+
"""
|
|
101
|
+
Builds a dotted JSON path for a validation error.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
exc (ValidationError): The JSON schema validation error.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
str: Dotted path for the failing instance location.
|
|
108
|
+
"""
|
|
109
|
+
return ".".join(str(each) for each in exc.absolute_path)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def load_json_file(data: Path, schema: dict, param_hint: str) -> Any:
|
|
113
|
+
"""
|
|
114
|
+
Loads a JSON CLI input file and validates it against a schema.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
data (Path): JSON file path.
|
|
118
|
+
schema (dict): JSON schema to validate against.
|
|
119
|
+
param_hint (str): CLI parameter associated with the JSON file.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Any: Parsed and validated JSON input.
|
|
123
|
+
|
|
124
|
+
Raises:
|
|
125
|
+
typer.BadParameter: Raised when the file cannot be loaded or validation
|
|
126
|
+
fails.
|
|
127
|
+
"""
|
|
128
|
+
try:
|
|
129
|
+
with open(data, "r", encoding="utf-8") as f:
|
|
130
|
+
input_data = json.load(f)
|
|
131
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
132
|
+
_cli_bad_parameter(
|
|
133
|
+
f"Invalid JSON file: {exc}",
|
|
134
|
+
param_hint=param_hint,
|
|
135
|
+
cause=exc,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
validate(instance=input_data, schema=schema)
|
|
140
|
+
except ValidationError as exc:
|
|
141
|
+
error_path = _validation_error_path(exc)
|
|
142
|
+
location = f" at {error_path}" if error_path else ""
|
|
143
|
+
_cli_bad_parameter(
|
|
144
|
+
f"Invalid JSON file{location}: {exc.message}",
|
|
145
|
+
param_hint=param_hint,
|
|
146
|
+
cause=exc,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
return input_data
|
|
150
|
+
|
|
151
|
+
|
|
98
152
|
def get_widget(wrapper: PdfWrapper, field: str, param_hint: str) -> Widget:
|
|
99
153
|
"""
|
|
100
154
|
Look up a widget and report missing names as CLI input errors.
|
|
@@ -113,7 +167,7 @@ def get_widget(wrapper: PdfWrapper, field: str, param_hint: str) -> Widget:
|
|
|
113
167
|
try:
|
|
114
168
|
return wrapper.widgets[field]
|
|
115
169
|
except KeyError as exc:
|
|
116
|
-
|
|
170
|
+
_cli_bad_parameter(
|
|
117
171
|
f"Form field '{field}' does not exist.",
|
|
118
172
|
param_hint=param_hint,
|
|
119
173
|
cause=exc,
|
|
@@ -150,8 +204,10 @@ def create_elements_from_file(
|
|
|
150
204
|
pdf: Path,
|
|
151
205
|
data: Path,
|
|
152
206
|
element_map: dict,
|
|
207
|
+
schema: dict,
|
|
153
208
|
method_name: str,
|
|
154
209
|
ctx: typer.Context,
|
|
210
|
+
param_hint: str,
|
|
155
211
|
output: Path | None = None,
|
|
156
212
|
) -> None:
|
|
157
213
|
"""
|
|
@@ -169,16 +225,17 @@ def create_elements_from_file(
|
|
|
169
225
|
definitions.
|
|
170
226
|
element_map (dict): Mapping from JSON group names to element classes or
|
|
171
227
|
callables used to construct each object.
|
|
228
|
+
schema (dict): JSON schema used to validate the grouped definitions.
|
|
172
229
|
method_name (str): Name of the `PdfWrapper` method that accepts the
|
|
173
230
|
constructed elements, such as `bulk_create_fields`, `draw`, or
|
|
174
231
|
`annotate`.
|
|
175
232
|
ctx (typer.Context): Typer context containing global wrapper options in
|
|
176
233
|
`ctx.obj`.
|
|
234
|
+
param_hint (str): CLI parameter associated with the JSON file.
|
|
177
235
|
output (Path, optional): Path where the modified PDF should be saved. If
|
|
178
236
|
omitted, the input PDF is overwritten. Defaults to None.
|
|
179
237
|
"""
|
|
180
|
-
|
|
181
|
-
input_data = json.load(f)
|
|
238
|
+
input_data = load_json_file(data, schema, param_hint)
|
|
182
239
|
|
|
183
240
|
obj = PdfWrapper(str(pdf), **ctx.obj)
|
|
184
241
|
ungrouped_input = []
|
|
@@ -18,6 +18,7 @@ from .. import (Annotations, BlankPage, Fields, PdfArray, PdfWrapper,
|
|
|
18
18
|
RawElements)
|
|
19
19
|
from .common import (INPUT_PDF, OPTIONAL_OUTPUT_PDF, REQUIRED_OUTPUT_PDF,
|
|
20
20
|
create_elements_from_file, json_file_option)
|
|
21
|
+
from .schemas.create import ANNOTATION_SCHEMA, FIELD_SCHEMA, RAW_SCHEMA
|
|
21
22
|
|
|
22
23
|
create_cli = typer.Typer(
|
|
23
24
|
context_settings={"help_option_names": ["--help", "-h"]}, no_args_is_help=True
|
|
@@ -132,7 +133,16 @@ def field(
|
|
|
132
133
|
"image": Fields.ImageField,
|
|
133
134
|
"signature": Fields.SignatureField,
|
|
134
135
|
}
|
|
135
|
-
create_elements_from_file(
|
|
136
|
+
create_elements_from_file(
|
|
137
|
+
pdf=pdf,
|
|
138
|
+
data=data,
|
|
139
|
+
element_map=field_map,
|
|
140
|
+
schema=FIELD_SCHEMA,
|
|
141
|
+
method_name="bulk_create_fields",
|
|
142
|
+
ctx=ctx,
|
|
143
|
+
param_hint="--file",
|
|
144
|
+
output=output,
|
|
145
|
+
)
|
|
136
146
|
|
|
137
147
|
|
|
138
148
|
@create_cli.command(no_args_is_help=True)
|
|
@@ -151,7 +161,16 @@ def raw(
|
|
|
151
161
|
"circle": RawElements.RawCircle,
|
|
152
162
|
"ellipse": RawElements.RawEllipse,
|
|
153
163
|
}
|
|
154
|
-
create_elements_from_file(
|
|
164
|
+
create_elements_from_file(
|
|
165
|
+
pdf=pdf,
|
|
166
|
+
data=data,
|
|
167
|
+
element_map=raw_element_map,
|
|
168
|
+
schema=RAW_SCHEMA,
|
|
169
|
+
method_name="draw",
|
|
170
|
+
ctx=ctx,
|
|
171
|
+
param_hint="--file",
|
|
172
|
+
output=output,
|
|
173
|
+
)
|
|
155
174
|
|
|
156
175
|
|
|
157
176
|
@create_cli.command(no_args_is_help=True)
|
|
@@ -171,7 +190,16 @@ def annotation(
|
|
|
171
190
|
"strikeout": Annotations.StrikeOutAnnotation,
|
|
172
191
|
"stamp": Annotations.RubberStampAnnotation,
|
|
173
192
|
}
|
|
174
|
-
create_elements_from_file(
|
|
193
|
+
create_elements_from_file(
|
|
194
|
+
pdf=pdf,
|
|
195
|
+
data=data,
|
|
196
|
+
element_map=annotation_map,
|
|
197
|
+
schema=ANNOTATION_SCHEMA,
|
|
198
|
+
method_name="annotate",
|
|
199
|
+
ctx=ctx,
|
|
200
|
+
param_hint="--file",
|
|
201
|
+
output=output,
|
|
202
|
+
)
|
|
175
203
|
|
|
176
204
|
|
|
177
205
|
@create_cli.command(no_args_is_help=True)
|