python-pdffiller 1.1.1__tar.gz → 2.0.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 (80) hide show
  1. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/PKG-INFO +13 -17
  2. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/README.rst +11 -15
  3. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/_version.py +1 -1
  4. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/cli/commands/dump_data_fields.py +2 -1
  5. python_pdffiller-2.0.0/pdffiller/pdf.py +221 -0
  6. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/typing.py +1 -2
  7. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pyproject.toml +3 -2
  8. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/python_pdffiller.egg-info/PKG-INFO +13 -17
  9. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/python_pdffiller.egg-info/SOURCES.txt +3 -1
  10. python_pdffiller-2.0.0/python_pdffiller.egg-info/requires.txt +3 -0
  11. python_pdffiller-2.0.0/requirements.txt +3 -0
  12. python_pdffiller-2.0.0/tests/cli/test_dump_data_field.py +106 -0
  13. python_pdffiller-2.0.0/tests/cli/test_fill_form.py +103 -0
  14. python_pdffiller-2.0.0/tests/conftest.py +70 -0
  15. python_pdffiller-2.0.0/tests/unit/__init__.py +0 -0
  16. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/tests/unit/test_form_field.py +6 -5
  17. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/tox.ini +17 -3
  18. python_pdffiller-1.1.1/pdffiller/io/custom_pdf_writer.py +0 -143
  19. python_pdffiller-1.1.1/pdffiller/pdf.py +0 -343
  20. python_pdffiller-1.1.1/python_pdffiller.egg-info/requires.txt +0 -3
  21. python_pdffiller-1.1.1/requirements.txt +0 -3
  22. python_pdffiller-1.1.1/tests/conftest.py +0 -28
  23. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/AUTHORS.rst +0 -0
  24. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/CHANGELOG.md +0 -0
  25. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/COPYING +0 -0
  26. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/MANIFEST.in +0 -0
  27. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/docs/Makefile +0 -0
  28. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/docs/make.bat +0 -0
  29. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/docs/source/__init__.py +0 -0
  30. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/docs/source/_static/rtd_literal_block.css +0 -0
  31. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/docs/source/_static/rtd_theme_overrides.css +0 -0
  32. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/docs/source/_static/terminal_output.css +0 -0
  33. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/docs/source/changelog.md +0 -0
  34. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/docs/source/cli-commands.rst +0 -0
  35. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/docs/source/cli-usage.rst +0 -0
  36. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/docs/source/commands/dump_data_fields.rst +0 -0
  37. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/docs/source/commands/fill_form.rst +0 -0
  38. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/docs/source/conf.py +0 -0
  39. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/docs/source/contributing.rst +0 -0
  40. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/docs/source/index.rst +0 -0
  41. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/__init__.py +0 -0
  42. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/__main__.py +0 -0
  43. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/cli/__init__.py +0 -0
  44. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/cli/args.py +0 -0
  45. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/cli/boolean_action.py +0 -0
  46. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/cli/cli.py +0 -0
  47. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/cli/command.py +0 -0
  48. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/cli/commands/__init__.py +0 -0
  49. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/cli/commands/fill_form.py +0 -0
  50. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/cli/exit_codes.py +0 -0
  51. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/cli/formatters.py +0 -0
  52. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/cli/once_argument.py +0 -0
  53. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/cli/smart_formatter.py +0 -0
  54. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/const.py +0 -0
  55. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/exceptions.py +0 -0
  56. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/io/__init__.py +0 -0
  57. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/io/colors.py +0 -0
  58. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/io/output.py +0 -0
  59. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/py.typed.py +0 -0
  60. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/utils.py +0 -0
  61. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/widgets/__init__.py +0 -0
  62. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/widgets/base.py +0 -0
  63. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/widgets/checkbox.py +0 -0
  64. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/widgets/radio.py +0 -0
  65. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/pdffiller/widgets/text.py +0 -0
  66. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/python_pdffiller.egg-info/dependency_links.txt +0 -0
  67. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/python_pdffiller.egg-info/entry_points.txt +0 -0
  68. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/python_pdffiller.egg-info/top_level.txt +0 -0
  69. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/requirements-dev.txt +0 -0
  70. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/requirements-doc.txt +0 -0
  71. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/requirements-lint.txt +0 -0
  72. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/requirements-test.txt +0 -0
  73. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/setup.cfg +0 -0
  74. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/setup.py +0 -0
  75. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/tests/__init__.py +0 -0
  76. {python_pdffiller-1.1.1/tests/unit → python_pdffiller-2.0.0/tests/cli}/__init__.py +0 -0
  77. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/tests/data/empty.pdf +0 -0
  78. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/tests/data/input.pdf +0 -0
  79. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/tests/unit/test_formatters.py +0 -0
  80. {python_pdffiller-1.1.1 → python_pdffiller-2.0.0}/tests/unit/test_setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-pdffiller
3
- Version: 1.1.1
3
+ Version: 2.0.0
4
4
  Summary: Interact with PDF by inspecting or filling it
5
5
  Author-email: Jacques Raphanel <jraphanel@sismic.fr>
6
6
  License-Expression: MIT
@@ -26,17 +26,17 @@ Requires-Python: >=3.9
26
26
  Description-Content-Type: text/x-rst
27
27
  License-File: COPYING
28
28
  License-File: AUTHORS.rst
29
- Requires-Dist: pypdf
29
+ Requires-Dist: pymupdf==1.24.10
30
30
  Requires-Dist: colorama
31
31
  Requires-Dist: pyyaml
32
32
  Dynamic: license-file
33
33
 
34
- pypdffiller
35
- ===========
34
+ pdffiller
35
+ =========
36
36
 
37
37
  |Test| |PyPI| |Python| |Code Style| |Pre-Commit| |License|
38
38
 
39
- ``pypdffiller`` is a free and open source pure-Python 3 library for PDF form processing. It contains the essential
39
+ ``python-pdffiller`` is a free and open source pure-Python 3 library for PDF form processing. It contains the essential
40
40
  functionalities needed to interact with PDF forms:
41
41
 
42
42
  - Inspect what data a PDF form needs to be filled with.
@@ -45,9 +45,9 @@ functionalities needed to interact with PDF forms:
45
45
  Installation
46
46
  ------------
47
47
 
48
- As of first version, ``pypdffiller`` is compatible with Python 3.9+.
48
+ As of first version, ``python-pdffiller`` is compatible with Python 3.9+.
49
49
 
50
- Use ``pip`` to install the latest stable version of ``pypdffiller``:
50
+ Use ``pip`` to install the latest stable version of ``python-pdffiller``:
51
51
 
52
52
  .. code-block:: console
53
53
 
@@ -72,7 +72,7 @@ https://github.com/sismicfr/pypdffiller/issues.
72
72
  Documentation
73
73
  -------------
74
74
 
75
- The full documentation for CLI and API is available at https://pypdffiller.readthedocs.org/en/stable/.
75
+ The full documentation for CLI and API is available at https://sismicfr.github.io/pypdffiller/
76
76
 
77
77
  Build the docs
78
78
  ~~~~~~~~~~~~~~
@@ -106,23 +106,19 @@ We use ``tox`` to manage our environment and build the executable:
106
106
  Contributing
107
107
  ------------
108
108
 
109
- For guidelines for contributing to ``pypdffiller``, refer to `CONTRIBUTING.rst <https://github.com/sismicfr/pypdffiller/blob/main/CONTRIBUTING.rst>`_.
109
+ For guidelines for contributing to ``python-pdffiller``, refer to `CONTRIBUTING.rst <https://github.com/sismicfr/pypdffiller/blob/main/CONTRIBUTING.rst>`_.
110
110
 
111
111
 
112
112
  .. |Test| image:: https://github.com/sismicfr/pypdffiller/workflows/Test/badge.svg
113
113
  :target: https://github.com/sismicfr/pypdffiller/actions
114
114
  :alt: Test
115
115
 
116
- .. |PyPI| image:: https://img.shields.io/pypi/v/pypdffiller?label=PyPI&logo=pypi
117
- :target: https://badge.fury.io/py/pypdffiller
116
+ .. |PyPI| image:: https://img.shields.io/pypi/v/python-pdffiller?label=PyPI&logo=pypi
117
+ :target: https://badge.fury.io/py/python-pdffiller
118
118
  :alt: PyPI
119
119
 
120
- .. |Read the Docs| image:: https://img.shields.io/readthedocs/pypdffiller?label=Documentation&logo=Read%20the%20Docs
121
- :target: https://sismicfr.github.io/pypdffiller
122
- :alt: Docs
123
-
124
- .. |Python| image:: https://img.shields.io/pypi/pyversions/pypdffiller.svg?label=Python&logo=Python
125
- :target: https://pypi.python.org/pypi/pypdffiller
120
+ .. |Python| image:: https://img.shields.io/pypi/pyversions/python-pdffiller.svg?label=Python&logo=Python
121
+ :target: https://pypi.python.org/pypi/python-pdffiller
126
122
  :alt: Python
127
123
 
128
124
  .. |Code Style| image:: https://img.shields.io/badge/code%20style-black-000000.svg?label=Code%20Style
@@ -1,9 +1,9 @@
1
- pypdffiller
2
- ===========
1
+ pdffiller
2
+ =========
3
3
 
4
4
  |Test| |PyPI| |Python| |Code Style| |Pre-Commit| |License|
5
5
 
6
- ``pypdffiller`` is a free and open source pure-Python 3 library for PDF form processing. It contains the essential
6
+ ``python-pdffiller`` is a free and open source pure-Python 3 library for PDF form processing. It contains the essential
7
7
  functionalities needed to interact with PDF forms:
8
8
 
9
9
  - Inspect what data a PDF form needs to be filled with.
@@ -12,9 +12,9 @@ functionalities needed to interact with PDF forms:
12
12
  Installation
13
13
  ------------
14
14
 
15
- As of first version, ``pypdffiller`` is compatible with Python 3.9+.
15
+ As of first version, ``python-pdffiller`` is compatible with Python 3.9+.
16
16
 
17
- Use ``pip`` to install the latest stable version of ``pypdffiller``:
17
+ Use ``pip`` to install the latest stable version of ``python-pdffiller``:
18
18
 
19
19
  .. code-block:: console
20
20
 
@@ -39,7 +39,7 @@ https://github.com/sismicfr/pypdffiller/issues.
39
39
  Documentation
40
40
  -------------
41
41
 
42
- The full documentation for CLI and API is available at https://pypdffiller.readthedocs.org/en/stable/.
42
+ The full documentation for CLI and API is available at https://sismicfr.github.io/pypdffiller/
43
43
 
44
44
  Build the docs
45
45
  ~~~~~~~~~~~~~~
@@ -73,23 +73,19 @@ We use ``tox`` to manage our environment and build the executable:
73
73
  Contributing
74
74
  ------------
75
75
 
76
- For guidelines for contributing to ``pypdffiller``, refer to `CONTRIBUTING.rst <https://github.com/sismicfr/pypdffiller/blob/main/CONTRIBUTING.rst>`_.
76
+ For guidelines for contributing to ``python-pdffiller``, refer to `CONTRIBUTING.rst <https://github.com/sismicfr/pypdffiller/blob/main/CONTRIBUTING.rst>`_.
77
77
 
78
78
 
79
79
  .. |Test| image:: https://github.com/sismicfr/pypdffiller/workflows/Test/badge.svg
80
80
  :target: https://github.com/sismicfr/pypdffiller/actions
81
81
  :alt: Test
82
82
 
83
- .. |PyPI| image:: https://img.shields.io/pypi/v/pypdffiller?label=PyPI&logo=pypi
84
- :target: https://badge.fury.io/py/pypdffiller
83
+ .. |PyPI| image:: https://img.shields.io/pypi/v/python-pdffiller?label=PyPI&logo=pypi
84
+ :target: https://badge.fury.io/py/python-pdffiller
85
85
  :alt: PyPI
86
86
 
87
- .. |Read the Docs| image:: https://img.shields.io/readthedocs/pypdffiller?label=Documentation&logo=Read%20the%20Docs
88
- :target: https://sismicfr.github.io/pypdffiller
89
- :alt: Docs
90
-
91
- .. |Python| image:: https://img.shields.io/pypi/pyversions/pypdffiller.svg?label=Python&logo=Python
92
- :target: https://pypi.python.org/pypi/pypdffiller
87
+ .. |Python| image:: https://img.shields.io/pypi/pyversions/python-pdffiller.svg?label=Python&logo=Python
88
+ :target: https://pypi.python.org/pypi/python-pdffiller
93
89
  :alt: Python
94
90
 
95
91
  .. |Code Style| image:: https://img.shields.io/badge/code%20style-black-000000.svg?label=Code%20Style
@@ -3,4 +3,4 @@ __copyright__ = "Copyright 2025 SISMIC"
3
3
  __email__ = "jraphanel@sismic.fr"
4
4
  __license__ = "MIT"
5
5
  __title__ = "pdffiller"
6
- __version__ = "1.1.1"
6
+ __version__ = "2.0.0"
@@ -30,7 +30,8 @@ def dump_fields_text_formatter(pdf: Pdf) -> None:
30
30
 
31
31
  def dump_fields_json_formatter(pdf: Pdf) -> None:
32
32
  """Print output text for dump_fields command as simple text"""
33
-
33
+ if not pdf:
34
+ return
34
35
  cli_out_write(json.dumps(pdf.schema, indent=4, ensure_ascii=False))
35
36
 
36
37
 
@@ -0,0 +1,221 @@
1
+ """
2
+ A module for wrapping PDF form operations, providing a high-level interface
3
+ for filling, and manipulating PDF forms.
4
+
5
+ This module simplifies common tasks such as:
6
+ - Filling PDF forms with data from a dictionary.
7
+ - Fetching PDF forms fields
8
+
9
+ The core class, `Pdf`, encapsulates a PDF document and provides
10
+ methods for interacting with its form fields and content.
11
+ """
12
+
13
+ from collections import OrderedDict
14
+
15
+ import pymupdf
16
+
17
+ from pdffiller.exceptions import PdfFillerException
18
+ from pdffiller.io.output import PdfFillerOutput
19
+
20
+ from .typing import Any, cast, Dict, List, Optional, PathLike, StreamType, Type
21
+ from .widgets.base import Widget
22
+ from .widgets.checkbox import CheckBoxWidget
23
+ from .widgets.radio import RadioWidget
24
+ from .widgets.text import TextWidget
25
+
26
+
27
+ class PdfAttributes: # pylint: disable=too-few-public-methods
28
+ """Various constants, enums, and flags to aid readability."""
29
+
30
+ READ_ONLY = 1 << 0
31
+
32
+
33
+ class Pdf:
34
+ """
35
+ A class to wrap PDF form operations, providing a simplified interface
36
+ for common tasks such as filling, creating, and manipulating PDF forms.
37
+
38
+ The `Pdf` class encapsulates a PDF document and provides methods
39
+ for interacting with its form fields (widgets) and content.
40
+
41
+ """
42
+
43
+ TYPE_TO_OBJECT: Dict[str, Type[Widget]] = {
44
+ "Text": TextWidget,
45
+ "RadioButton": RadioWidget,
46
+ "CheckBox": CheckBoxWidget,
47
+ }
48
+
49
+ def __init__(
50
+ self, filename: Optional[PathLike] = None, stream: Optional[StreamType] = None
51
+ ) -> None:
52
+ """
53
+ Constructor method for the `Pdf` class.
54
+
55
+ Initializes a new `Pdf` object with the given template PDF and optional keyword arguments.
56
+
57
+ Args:
58
+ filename (Optional[PathLike]): Path to the input pdf
59
+ stream (Optional[StreamType]): An open file-like object containing the PDF data.
60
+ """
61
+
62
+ super().__init__()
63
+ self.widgets: OrderedDict[str, Widget] = OrderedDict()
64
+ self._init_helper(filename, stream)
65
+
66
+ def _init_helper(
67
+ self, filename: Optional[PathLike] = None, stream: Optional[StreamType] = None
68
+ ) -> None:
69
+ """
70
+ Helper method to initialize widgets
71
+
72
+ Args:
73
+ filename (Optional[PathLike]): Path to the input pdf
74
+ stream (Optional[StreamType]): An open file-like object containing the PDF data.
75
+ """
76
+ if not filename and not stream:
77
+ return
78
+
79
+ output = PdfFillerOutput()
80
+ output.info("loading file in memory")
81
+ loaded_widgets: OrderedDict[str, Widget] = OrderedDict()
82
+ try:
83
+ doc = pymupdf.open(filename=filename, stream=stream)
84
+ except Exception as ex: # pylint: disable=broad-exception-caught
85
+ PdfFillerOutput().error(str(ex))
86
+ raise PdfFillerException(
87
+ f"failed to load {filename or 'file from input string'}"
88
+ ) from ex
89
+
90
+ for i, page in enumerate(doc.pages()):
91
+ output.verbose(f"loading page {i+1}/{doc.page_count}")
92
+ for widget in page.widgets():
93
+ button_states = widget.button_states()
94
+ choices = button_states["normal"] if button_states else None
95
+
96
+ if widget.field_name not in loaded_widgets:
97
+ if widget.field_type_string not in self.TYPE_TO_OBJECT:
98
+ output.verbose(f"unsupported {widget.field_type_string} widget type")
99
+ continue
100
+ new_widget = self.TYPE_TO_OBJECT[widget.field_type_string](
101
+ widget.field_name, i, widget.field_value, widget.field_flags & (1 << 0)
102
+ )
103
+ if choices and isinstance(new_widget, CheckBoxWidget):
104
+ new_widget.choices = choices
105
+ elif isinstance(new_widget, TextWidget):
106
+ new_widget.max_length = widget.text_maxlen
107
+ loaded_widgets[widget.field_name] = new_widget
108
+ else:
109
+ new_widget = loaded_widgets[widget.field_name]
110
+ if choices and isinstance(new_widget, CheckBoxWidget):
111
+ for each in choices:
112
+ if new_widget.choices is not None:
113
+ if each not in new_widget.choices:
114
+ new_widget.choices.append(each)
115
+ else:
116
+ new_widget.choices = [each]
117
+
118
+ cast(CheckBoxWidget, loaded_widgets[widget.field_name]).choices = (
119
+ new_widget.choices
120
+ )
121
+
122
+ self.widgets = loaded_widgets
123
+
124
+ @property
125
+ def schema(self) -> List[Dict[str, Any]]:
126
+ """
127
+ Returns the JSON schema of the PDF form, describing the structure and data
128
+ types of the form fields.
129
+
130
+ This schema can be used to generate user interfaces or validate data before
131
+ filling the form.
132
+
133
+ Returns:
134
+ dict: A dictionary representing the JSON schema of the PDF form.
135
+ """
136
+
137
+ return [widget.schema_definition for widget in self.widgets.values()]
138
+
139
+ def fill(
140
+ self,
141
+ input_file: PathLike,
142
+ output_file: PathLike,
143
+ data: Dict[str, str],
144
+ flatten: bool = True,
145
+ ) -> "Pdf":
146
+ """
147
+ Fill the PDF form with data from a dictionary.
148
+
149
+ Args:
150
+ input_file (PathLike): The input file path.
151
+ output_file (PathLike): The output file path.
152
+ data (Dict[str, Union[str, bool, int]]): A dictionary where keys are form field names
153
+ and values are the data to fill the fields with. Values can be strings, booleans,
154
+ or integers.
155
+ flatten (bool): Whether to flatten the form after filling, making the fields read-only
156
+ (default: False).
157
+
158
+ Returns:
159
+ Pdf: The `Pdf` object, allowing for method chaining.
160
+ """
161
+ try:
162
+ document = pymupdf.open(filename=input_file)
163
+ except Exception as ex:
164
+ PdfFillerOutput().error(str(ex))
165
+ raise PdfFillerException(f"failed to open {input_file}") from ex
166
+
167
+ output = PdfFillerOutput()
168
+
169
+ output.info("filling pdf with input values")
170
+ # Iterate over all pages and process fields
171
+ for page in document:
172
+ for field in page.widgets():
173
+ if field.field_name in data:
174
+ value = data[field.field_name]
175
+
176
+ # Handling checkboxes
177
+ if (
178
+ field.field_type
179
+ == pymupdf.PDF_WIDGET_TYPE_CHECKBOX # pylint: disable=no-member
180
+ ):
181
+ print(f"{field.field_name} => {field.on_state()} vs {value}")
182
+ if value.strip() and "Off" != value.strip():
183
+ output.verbose(
184
+ f"updating checkbox with {value} from {field.field_value}"
185
+ )
186
+ field.field_value = True
187
+ else:
188
+ field.field_value = False
189
+
190
+ # Handling radio buttons
191
+ elif (
192
+ field.field_type
193
+ == pymupdf.PDF_WIDGET_TYPE_RADIOBUTTON # pylint: disable=no-member
194
+ ):
195
+ if value == field.on_state():
196
+ output.verbose(
197
+ f"updating radiobutton with {value} from {field.field_value}"
198
+ )
199
+ field.field_value = value
200
+
201
+ # Handling other fields types
202
+ else:
203
+ output.verbose(
204
+ f"updating {field.field_name} with {value} from {field.field_value}"
205
+ )
206
+ field.field_value = value
207
+
208
+ # Update the widget!
209
+ field.update()
210
+
211
+ try:
212
+ if flatten:
213
+ output.info("remove all annotations")
214
+ document.bake(annots=False)
215
+
216
+ # Save the modified PDF
217
+ document.save(output_file)
218
+ except Exception: # pylint: disable=broad-exception-caught
219
+ output.warning("an error occurs when saving file")
220
+
221
+ return self
@@ -43,7 +43,7 @@ __all__ = [
43
43
  "Type",
44
44
  "Union",
45
45
  "SubParserType",
46
- "StrByteType",
46
+ "StreamType",
47
47
  "TextIO",
48
48
  ]
49
49
 
@@ -52,7 +52,6 @@ ExitCode = Union[str, int, None]
52
52
  PathLike = Union[str, Path]
53
53
 
54
54
  StreamType = IO[Any]
55
- StrByteType = Union[PathLike, StreamType]
56
55
 
57
56
  if TYPE_CHECKING:
58
57
  # pylint: disable=protected-access,line-too-long
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-pdffiller"
7
- version = "1.1.1"
7
+ version = "2.0.0"
8
8
  description="Interact with PDF by inspecting or filling it"
9
9
  requires-python = ">=3.9"
10
10
  license ="MIT"
@@ -26,7 +26,7 @@ classifiers = [
26
26
  readme = "README.rst"
27
27
  authors = [{ name = "Jacques Raphanel", email = "jraphanel@sismic.fr" }]
28
28
  keywords = ["development", "pdf"]
29
- dependencies = ["pypdf", "colorama", "pyyaml"]
29
+ dependencies = ["pymupdf==1.24.10", "colorama", "pyyaml"]
30
30
 
31
31
  [project.scripts]
32
32
  pdffiller = "pdffiller.cli:main"
@@ -92,6 +92,7 @@ logging_use_named_masks = false
92
92
  major_on_zero = true
93
93
  tag_format = "v{version}"
94
94
  build_command = """
95
+ apt update && apt install -y --no-install-recommends pkg-config gcc g++ swig && \
95
96
  python -m pip install build -rrequirements.txt && \
96
97
  python -m build . && \
97
98
  python installer/build.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-pdffiller
3
- Version: 1.1.1
3
+ Version: 2.0.0
4
4
  Summary: Interact with PDF by inspecting or filling it
5
5
  Author-email: Jacques Raphanel <jraphanel@sismic.fr>
6
6
  License-Expression: MIT
@@ -26,17 +26,17 @@ Requires-Python: >=3.9
26
26
  Description-Content-Type: text/x-rst
27
27
  License-File: COPYING
28
28
  License-File: AUTHORS.rst
29
- Requires-Dist: pypdf
29
+ Requires-Dist: pymupdf==1.24.10
30
30
  Requires-Dist: colorama
31
31
  Requires-Dist: pyyaml
32
32
  Dynamic: license-file
33
33
 
34
- pypdffiller
35
- ===========
34
+ pdffiller
35
+ =========
36
36
 
37
37
  |Test| |PyPI| |Python| |Code Style| |Pre-Commit| |License|
38
38
 
39
- ``pypdffiller`` is a free and open source pure-Python 3 library for PDF form processing. It contains the essential
39
+ ``python-pdffiller`` is a free and open source pure-Python 3 library for PDF form processing. It contains the essential
40
40
  functionalities needed to interact with PDF forms:
41
41
 
42
42
  - Inspect what data a PDF form needs to be filled with.
@@ -45,9 +45,9 @@ functionalities needed to interact with PDF forms:
45
45
  Installation
46
46
  ------------
47
47
 
48
- As of first version, ``pypdffiller`` is compatible with Python 3.9+.
48
+ As of first version, ``python-pdffiller`` is compatible with Python 3.9+.
49
49
 
50
- Use ``pip`` to install the latest stable version of ``pypdffiller``:
50
+ Use ``pip`` to install the latest stable version of ``python-pdffiller``:
51
51
 
52
52
  .. code-block:: console
53
53
 
@@ -72,7 +72,7 @@ https://github.com/sismicfr/pypdffiller/issues.
72
72
  Documentation
73
73
  -------------
74
74
 
75
- The full documentation for CLI and API is available at https://pypdffiller.readthedocs.org/en/stable/.
75
+ The full documentation for CLI and API is available at https://sismicfr.github.io/pypdffiller/
76
76
 
77
77
  Build the docs
78
78
  ~~~~~~~~~~~~~~
@@ -106,23 +106,19 @@ We use ``tox`` to manage our environment and build the executable:
106
106
  Contributing
107
107
  ------------
108
108
 
109
- For guidelines for contributing to ``pypdffiller``, refer to `CONTRIBUTING.rst <https://github.com/sismicfr/pypdffiller/blob/main/CONTRIBUTING.rst>`_.
109
+ For guidelines for contributing to ``python-pdffiller``, refer to `CONTRIBUTING.rst <https://github.com/sismicfr/pypdffiller/blob/main/CONTRIBUTING.rst>`_.
110
110
 
111
111
 
112
112
  .. |Test| image:: https://github.com/sismicfr/pypdffiller/workflows/Test/badge.svg
113
113
  :target: https://github.com/sismicfr/pypdffiller/actions
114
114
  :alt: Test
115
115
 
116
- .. |PyPI| image:: https://img.shields.io/pypi/v/pypdffiller?label=PyPI&logo=pypi
117
- :target: https://badge.fury.io/py/pypdffiller
116
+ .. |PyPI| image:: https://img.shields.io/pypi/v/python-pdffiller?label=PyPI&logo=pypi
117
+ :target: https://badge.fury.io/py/python-pdffiller
118
118
  :alt: PyPI
119
119
 
120
- .. |Read the Docs| image:: https://img.shields.io/readthedocs/pypdffiller?label=Documentation&logo=Read%20the%20Docs
121
- :target: https://sismicfr.github.io/pypdffiller
122
- :alt: Docs
123
-
124
- .. |Python| image:: https://img.shields.io/pypi/pyversions/pypdffiller.svg?label=Python&logo=Python
125
- :target: https://pypi.python.org/pypi/pypdffiller
120
+ .. |Python| image:: https://img.shields.io/pypi/pyversions/python-pdffiller.svg?label=Python&logo=Python
121
+ :target: https://pypi.python.org/pypi/python-pdffiller
126
122
  :alt: Python
127
123
 
128
124
  .. |Code Style| image:: https://img.shields.io/badge/code%20style-black-000000.svg?label=Code%20Style
@@ -48,7 +48,6 @@ pdffiller/cli/commands/dump_data_fields.py
48
48
  pdffiller/cli/commands/fill_form.py
49
49
  pdffiller/io/__init__.py
50
50
  pdffiller/io/colors.py
51
- pdffiller/io/custom_pdf_writer.py
52
51
  pdffiller/io/output.py
53
52
  pdffiller/widgets/__init__.py
54
53
  pdffiller/widgets/base.py
@@ -63,6 +62,9 @@ python_pdffiller.egg-info/requires.txt
63
62
  python_pdffiller.egg-info/top_level.txt
64
63
  tests/__init__.py
65
64
  tests/conftest.py
65
+ tests/cli/__init__.py
66
+ tests/cli/test_dump_data_field.py
67
+ tests/cli/test_fill_form.py
66
68
  tests/data/empty.pdf
67
69
  tests/data/input.pdf
68
70
  tests/unit/__init__.py
@@ -0,0 +1,3 @@
1
+ pymupdf==1.24.10
2
+ colorama
3
+ pyyaml
@@ -0,0 +1,3 @@
1
+ pymupdf==1.24.10
2
+ colorama
3
+ pyyaml
@@ -0,0 +1,106 @@
1
+ import json
2
+ from unittest import mock
3
+
4
+ import pytest
5
+
6
+ from pdffiller.cli import cli
7
+ from pdffiller.cli.exit_codes import (
8
+ ERROR_COMMAND_NAME,
9
+ ERROR_UNEXPECTED,
10
+ SUCCESS,
11
+ )
12
+
13
+
14
+ def test_incomplete_no_action():
15
+ """test empty command-line"""
16
+
17
+ with mock.patch("sys.argv", []):
18
+ assert cli.main() == ERROR_COMMAND_NAME
19
+
20
+
21
+ @pytest.mark.parametrize("argv", [])
22
+ def test_incomplete(argv):
23
+ """test empty command-line"""
24
+
25
+ # Test through direct command-line
26
+ with mock.patch("sys.argv", ["pdffiller", "dump_data_fields"] + argv):
27
+ assert cli.main() == ERROR_UNEXPECTED
28
+
29
+ # Test with direct call to main function
30
+ assert cli.main(["dump_data_fields"] + argv) == ERROR_UNEXPECTED
31
+
32
+
33
+ def test_complete(test_data_dir, capsys):
34
+ """test complete command-line"""
35
+
36
+ argv = [
37
+ str(test_data_dir / "input.pdf"),
38
+ ]
39
+
40
+ json_fields = """[
41
+ {
42
+ "FieldType": "text",
43
+ "FieldName": "Lastname"
44
+ },
45
+ {
46
+ "FieldType": "text",
47
+ "FieldName": "Firstname"
48
+ },
49
+ {
50
+ "FieldType": "checkbox",
51
+ "FieldOptions": [
52
+ "Off",
53
+ "Oui"
54
+ ],
55
+ "FieldName": "Men"
56
+ },
57
+ {
58
+ "FieldType": "checkbox",
59
+ "FieldOptions": [
60
+ "Off",
61
+ "Oui"
62
+ ],
63
+ "FieldName": "Women"
64
+ },
65
+ {
66
+ "FieldType": "radio",
67
+ "FieldOptions": [
68
+ "Off",
69
+ "Single",
70
+ "Married",
71
+ "Divorced"
72
+ ],
73
+ "FieldName": "MaritalStatus",
74
+ "FieldValue": "Off"
75
+ }
76
+ ]"""
77
+ # Test through direct command-line
78
+ with mock.patch(
79
+ "sys.argv",
80
+ ["pdffiller", "dump_data_fields", "-fjson"] + argv,
81
+ ):
82
+ assert cli.main() == SUCCESS
83
+ out, err = capsys.readouterr()
84
+ assert "loading file in memory" == err.strip()
85
+ assert json.loads(json_fields) == json.loads(out)
86
+
87
+ # Test with direct call to main function
88
+ assert cli.main(["dump_data_fields"] + argv) == SUCCESS
89
+
90
+
91
+ def test_complete_with_invalid_file(test_data_dir):
92
+ """test complete command-line"""
93
+
94
+ argv = [
95
+ str(test_data_dir / "empty.pdf"),
96
+ ]
97
+
98
+ # Test through direct command-line
99
+ with mock.patch(
100
+ "sys.argv",
101
+ ["pdffiller", "dump_data_fields"] + argv,
102
+ ):
103
+ assert cli.main() == ERROR_UNEXPECTED
104
+
105
+ # Test with direct call to main function
106
+ assert cli.main(["dump_data_fields"] + argv) == ERROR_UNEXPECTED