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.
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PKG-INFO +7 -7
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/__init__.py +1 -1
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/cli/__init__.py +58 -1
- pypdfform-4.8.0/PyPDFForm/cli/coordinate.py +165 -0
- pypdfform-4.8.0/PyPDFForm/cli/create.py +169 -0
- pypdfform-4.8.0/PyPDFForm/cli/inspect.py +53 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm.egg-info/PKG-INFO +7 -7
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm.egg-info/SOURCES.txt +2 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm.egg-info/requires.txt +6 -6
- {pypdfform-4.7.10 → pypdfform-4.8.0}/pyproject.toml +6 -6
- {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_cli.py +24 -0
- pypdfform-4.7.10/PyPDFForm/cli/coordinate.py +0 -80
- {pypdfform-4.7.10 → pypdfform-4.8.0}/LICENSE +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/cli/update.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/__init__.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/adapter.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/annotations/__init__.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/annotations/base.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/annotations/link.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/annotations/stamp.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/annotations/text.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/annotations/text_markup.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/assets/__init__.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/assets/bedrock.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/assets/blank.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/constants.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/coordinate.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/deprecation.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/egress.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/filler.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/font.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/hooks.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/image.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/middleware/__init__.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/middleware/base.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/middleware/checkbox.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/middleware/dropdown.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/middleware/image.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/middleware/radio.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/middleware/signature.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/middleware/text.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/patterns.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/raw/__init__.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/raw/circle.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/raw/ellipse.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/raw/image.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/raw/line.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/raw/rect.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/raw/text.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/template.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/types.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/utils.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/watermark.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/widgets/__init__.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/widgets/base.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/widgets/checkbox.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/widgets/dropdown.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/widgets/image.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/widgets/radio.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/widgets/signature.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/widgets/text.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm/lib/wrapper.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm.egg-info/dependency_links.txt +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm.egg-info/entry_points.txt +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/PyPDFForm.egg-info/top_level.txt +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/README.md +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/setup.cfg +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_bulk_create_fields.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_create_widget.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_draw_elements.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_dropdown.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_extract_middleware_attributes.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_fill_max_length_text_field.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_font_widths.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_functional.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_generate_appearance_streams.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_js.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_need_appearances.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_paragraph.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_signature.py +0 -0
- {pypdfform-4.7.10 → pypdfform-4.8.0}/tests/test_use_full_widget_name.py +0 -0
- {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.
|
|
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.
|
|
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<
|
|
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,>=
|
|
35
|
-
Requires-Dist: jsonschema<5.0.0,>=4.
|
|
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.
|
|
43
|
-
Requires-Dist: requests<3.0.0,>=2.
|
|
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
|
|
|
@@ -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.
|
|
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.
|
|
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<
|
|
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,>=
|
|
35
|
-
Requires-Dist: jsonschema<5.0.0,>=4.
|
|
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.
|
|
43
|
-
Requires-Dist: requests<3.0.0,>=2.
|
|
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.
|
|
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<
|
|
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,>=
|
|
15
|
-
jsonschema<5.0.0,>=4.
|
|
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.
|
|
23
|
-
requests<3.0.0,>=2.
|
|
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.
|
|
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.
|
|
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>=
|
|
46
|
-
"jsonschema>=4.
|
|
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.
|
|
54
|
-
"requests>=2.
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|