python-pdffiller 1.0.0__py3-none-any.whl

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.
pdffiller/pdf.py ADDED
@@ -0,0 +1,488 @@
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
+ from pypdf import PdfReader, PdfWriter
16
+ from pypdf.errors import PyPdfError
17
+ from pypdf.generic import (
18
+ ArrayObject,
19
+ DictionaryObject,
20
+ NameObject,
21
+ NumberObject,
22
+ TextStringObject,
23
+ )
24
+
25
+ from pdffiller.io.output import PdfFillerOutput
26
+
27
+ from .typing import (
28
+ Any,
29
+ Callable,
30
+ cast,
31
+ Dict,
32
+ List,
33
+ Optional,
34
+ PathLike,
35
+ StrByteType,
36
+ Type,
37
+ Union,
38
+ )
39
+ from .widgets.base import Widget
40
+ from .widgets.checkbox import CheckBoxWidget
41
+ from .widgets.radio import RadioWidget
42
+ from .widgets.text import TextWidget
43
+
44
+
45
+ class PdfAttributes: # pylint: disable=too-few-public-methods
46
+ """Various constants, enums, and flags to aid readability."""
47
+
48
+ Widget = "/Widget"
49
+ Subtype = "/Subtype"
50
+ Parent = "/Parent"
51
+ T = "/T"
52
+ V = "/V"
53
+ AS = "/AS"
54
+ Kids = "/Kids"
55
+ AP = "/AP"
56
+ N = "/N"
57
+ D = "/D"
58
+ Opt = "/Opt"
59
+ MaxLen = "/MaxLen"
60
+ AcroForm = "/AcroForm"
61
+ Root = "/Root"
62
+ XFA = "/XFA"
63
+ FT = "/FT"
64
+ Ff = "/Ff"
65
+ Tx = "/Tx"
66
+ Ch = "/Ch"
67
+ Btn = "/Btn"
68
+ Off = "/Off"
69
+ READ_ONLY = 1 << 0
70
+
71
+
72
+ class Pdf:
73
+ """
74
+ A class to wrap PDF form operations, providing a simplified interface
75
+ for common tasks such as filling, creating, and manipulating PDF forms.
76
+
77
+ The `Pdf` class encapsulates a PDF document and provides methods
78
+ for interacting with its form fields (widgets) and content.
79
+
80
+ """
81
+
82
+ TYPE_TO_OBJECT: Dict[str, Type[Widget]] = {
83
+ "text": TextWidget,
84
+ "radio": RadioWidget,
85
+ "checkbox": CheckBoxWidget,
86
+ }
87
+
88
+ def __init__(
89
+ self,
90
+ content: Optional[StrByteType] = None,
91
+ adode_mode: Optional[bool] = True,
92
+ ) -> None:
93
+ """
94
+ Constructor method for the `Pdf` class.
95
+
96
+ Initializes a new `Pdf` object with the given template PDF and optional keyword arguments.
97
+
98
+ Args:
99
+ content (Optional[StrByteType]): The template PDF, provided as either:
100
+ - str: The file path to the PDF.
101
+ - BinaryIO: An open file-like object containing the PDF data.
102
+ adobe_mode (bool): Whether to enable Adobe-specific compatibility mode.
103
+ """
104
+
105
+ super().__init__()
106
+ self.widgets: OrderedDict[str, Widget] = OrderedDict()
107
+ self.adobe_mode = adode_mode
108
+
109
+ self._init_helper(content)
110
+
111
+ def _init_helper(self, content: Optional[StrByteType] = None) -> None:
112
+ """
113
+ Helper method to initialize widgets
114
+
115
+ This method is called during initialization and after certain operations
116
+ that modify the PDF content.
117
+ It rebuilds the widget dictionary.
118
+
119
+ Args:
120
+ content (Optional[StrByteType]): The template PDF, provided as either:
121
+ - str: The file path to the PDF.
122
+ - BinaryIO: An open file-like object containing the PDF data.
123
+ """
124
+ if not content:
125
+ return
126
+
127
+ loaded_widgets: OrderedDict[str, Widget] = OrderedDict()
128
+ try:
129
+ pdf_file = PdfReader(content)
130
+ except PyPdfError as ex:
131
+ PdfFillerOutput().error(str(ex))
132
+ return
133
+
134
+ for i, page in enumerate(pdf_file.pages):
135
+ widgets: Optional[ArrayObject] = page.annotations
136
+ if not widgets:
137
+ continue
138
+ for widget in widgets:
139
+ choices: Optional[List[str]] = None
140
+ if (
141
+ PdfAttributes.Subtype not in widget
142
+ or widget[PdfAttributes.Subtype] != PdfAttributes.Widget
143
+ ):
144
+ continue
145
+
146
+ if PdfAttributes.T not in widget:
147
+ widget = widget[PdfAttributes.Parent]
148
+ key = self._get_widget_name(widget)
149
+ if not key:
150
+ continue
151
+ widget_type: Optional[str] = self._get_field_type(widget)
152
+ if not widget_type:
153
+ continue
154
+
155
+ value = widget[PdfAttributes.V] if PdfAttributes.V in widget else None
156
+ if widget_type == "radio":
157
+ if value:
158
+ value = value[1:]
159
+ choices = []
160
+ if PdfAttributes.Kids in widget:
161
+ for each in widget[PdfAttributes.Kids]:
162
+ for each in each[PdfAttributes.AP][PdfAttributes.N].keys():
163
+ if each[1:] not in choices:
164
+ choices.append(each[1:])
165
+
166
+ elif widget_type == "checkbox":
167
+
168
+ if (
169
+ PdfAttributes.AP in widget
170
+ and PdfAttributes.N in widget[PdfAttributes.AP]
171
+ and PdfAttributes.D in widget[PdfAttributes.AP]
172
+ and PdfAttributes.AS in widget
173
+ ):
174
+ choices = [
175
+ each[1:] for each in (widget[PdfAttributes.AP][PdfAttributes.N]).keys()
176
+ ]
177
+ if "Off" not in choices:
178
+ choices.insert(0, "Off")
179
+ elif widget_type in ["list", "combo"] and value:
180
+ choices = [each[1:] for each in widget[PdfAttributes.Opt]]
181
+ if key not in loaded_widgets:
182
+ new_widget = self.TYPE_TO_OBJECT[widget_type](key, i, value)
183
+ if choices and isinstance(new_widget, CheckBoxWidget):
184
+ new_widget.choices = choices
185
+ elif isinstance(new_widget, TextWidget):
186
+ max_length = (
187
+ int(widget[PdfAttributes.MaxLen])
188
+ if PdfAttributes.MaxLen in widget
189
+ else None
190
+ )
191
+ if max_length:
192
+ new_widget.max_length = max_length
193
+ loaded_widgets[key] = new_widget
194
+ else:
195
+ new_widget = loaded_widgets[key]
196
+ if choices and isinstance(new_widget, CheckBoxWidget):
197
+ for each in choices:
198
+ if new_widget.choices is not None:
199
+ if each not in new_widget.choices:
200
+ new_widget.choices.append(each)
201
+ else:
202
+ new_widget.choices = [each]
203
+
204
+ cast(CheckBoxWidget, loaded_widgets[key]).choices = new_widget.choices
205
+
206
+ self.widgets = loaded_widgets
207
+
208
+ @property
209
+ def schema(self) -> List[Dict[str, Any]]:
210
+ """
211
+ Returns the JSON schema of the PDF form, describing the structure and data
212
+ types of the form fields.
213
+
214
+ This schema can be used to generate user interfaces or validate data before
215
+ filling the form.
216
+
217
+ Returns:
218
+ dict: A dictionary representing the JSON schema of the PDF form.
219
+ """
220
+
221
+ return [widget.schema_definition for widget in self.widgets.values()]
222
+
223
+ def fill(
224
+ self,
225
+ input_file: StrByteType,
226
+ output_file: PathLike,
227
+ data: Dict[str, Union[str, int, float, bool]],
228
+ flatten: bool = True,
229
+ ) -> "Pdf":
230
+ """
231
+ Fill the PDF form with data from a dictionary.
232
+
233
+ Args:
234
+ input_file (StrByteType): The template PDF, provided as either:
235
+ - str: The file path to the PDF.
236
+ - BinaryIO: An open file-like object containing the PDF data.
237
+ output_file (PathLike): The output file path.
238
+ data (Dict[str, Union[str, bool, int]]): A dictionary where keys are form field names
239
+ and values are the data to fill the fields with. Values can be strings, booleans,
240
+ or integers.
241
+ flatten (bool): Whether to flatten the form after filling, making the fields read-only
242
+ (default: False).
243
+
244
+ Returns:
245
+ Pdf: The `Pdf` object, allowing for method chaining.
246
+ """
247
+ for key, value in data.items():
248
+ if key in self.widgets:
249
+ self.widgets[key].value = value
250
+
251
+ reader = PdfReader(input_file)
252
+ if self.adobe_mode:
253
+ if (
254
+ PdfAttributes.AcroForm in cast(Any, reader.trailer[PdfAttributes.Root])
255
+ and PdfAttributes.XFA
256
+ in cast(Any, reader.trailer[PdfAttributes.Root])[PdfAttributes.AcroForm]
257
+ ):
258
+ del cast(Any, reader.trailer[PdfAttributes.Root])[PdfAttributes.AcroForm][
259
+ PdfAttributes.XFA
260
+ ]
261
+
262
+ writer = PdfWriter(reader)
263
+ if self.adobe_mode:
264
+ writer.set_need_appearances_writer()
265
+
266
+ fillers: Dict[str, Callable[[DictionaryObject, Any], None]] = {
267
+ "checkbox": self._fill_checkbox,
268
+ "text": self._fill_text,
269
+ "radio": self._fill_radio,
270
+ }
271
+
272
+ output = PdfFillerOutput()
273
+ for page in writer.pages:
274
+ widgets: Optional[ArrayObject] = page.annotations
275
+ if not widgets:
276
+ continue
277
+ for widget in widgets:
278
+ if (
279
+ PdfAttributes.Subtype not in widget
280
+ or widget[PdfAttributes.Subtype] != PdfAttributes.Widget
281
+ ):
282
+ continue
283
+
284
+ annotation = cast(DictionaryObject, widget.get_object())
285
+ if PdfAttributes.T not in widget:
286
+ widget = widget[PdfAttributes.Parent]
287
+ widget_key: Optional[str] = self._get_widget_name(widget)
288
+ if not widget_key or widget_key not in self.widgets:
289
+ continue
290
+
291
+ widget_type: Optional[str] = self._get_field_type(widget)
292
+ if not widget_type:
293
+ continue
294
+
295
+ if widget_type != "radio":
296
+ self.flatten_generic(annotation, flatten)
297
+ else:
298
+ self.flatten_radio(annotation, flatten)
299
+
300
+ if widget_key not in data or widget_type not in fillers:
301
+ continue
302
+
303
+ current_widget = self.widgets[widget_key]
304
+ if current_widget.value is None:
305
+ continue
306
+
307
+ output.debug(f"Filling {current_widget.name} with {current_widget.value}")
308
+ fillers[widget_type](annotation, current_widget)
309
+
310
+ with open(output_file, "wb") as f:
311
+ writer.write(f)
312
+
313
+ return self
314
+
315
+ def _get_widget_name(self, widget: Any) -> Optional[str]:
316
+ if PdfAttributes.T not in widget:
317
+ return None
318
+ key: Optional[str] = widget[PdfAttributes.T]
319
+ if (
320
+ PdfAttributes.Parent in widget
321
+ and PdfAttributes.T in widget[PdfAttributes.Parent].get_object()
322
+ and widget[PdfAttributes.Parent].get_object()[PdfAttributes.T] != key
323
+ ):
324
+ key = f"{self._get_widget_name(widget[PdfAttributes.Parent].get_object())}.{key}"
325
+
326
+ return key
327
+
328
+ def _get_field_type(self, annotation: Any) -> Optional[str]:
329
+ """
330
+ Determine widget type given its annotations.
331
+ """
332
+ ft = annotation[PdfAttributes.FT] if PdfAttributes.FT in annotation else None
333
+ ff = annotation[PdfAttributes.Ff] if PdfAttributes.Ff in annotation else None
334
+
335
+ if ft == PdfAttributes.Tx:
336
+ return "text"
337
+ if ft == PdfAttributes.Ch:
338
+ if ff and int(ff) & 1 << 17: # test 18th bit
339
+ return "combo"
340
+ return "list"
341
+ if ft == PdfAttributes.Btn:
342
+ if ff and int(ff) & 1 << 15: # test 16th bit
343
+ return "radio"
344
+ return "checkbox"
345
+
346
+ return None
347
+
348
+ @staticmethod
349
+ def _fill_text(annotation: DictionaryObject, widget: TextWidget) -> None:
350
+ """
351
+ Updates the value of a text annotation, setting the text content.
352
+
353
+ This function modifies the value (V) and appearance (AP) of the text
354
+ annotation to reflect the new text content.
355
+
356
+ Args:
357
+ annotation (DictionaryObject): The text annotation dictionary.
358
+ widget (TextWidget): The Text widget object containing the text value.
359
+ """
360
+ if PdfAttributes.Parent in annotation and PdfAttributes.T not in annotation:
361
+ annotation[NameObject(PdfAttributes.Parent)] = TextStringObject(widget.value)
362
+ annotation[NameObject(PdfAttributes.AP)] = TextStringObject(widget.value)
363
+ else:
364
+ annotation[NameObject(PdfAttributes.V)] = TextStringObject(widget.value)
365
+ annotation[NameObject(PdfAttributes.AP)] = TextStringObject(widget.value)
366
+
367
+ @staticmethod
368
+ def _fill_checkbox(annotation: DictionaryObject, widget: CheckBoxWidget) -> None:
369
+ value: Union[bool, str, None] = widget.value
370
+ if value is None:
371
+ value = False
372
+ if not isinstance(value, bool):
373
+ annotation[NameObject(PdfAttributes.AS)] = NameObject("/" + widget.value)
374
+ annotation[NameObject(PdfAttributes.V)] = NameObject("/" + widget.value)
375
+ return
376
+
377
+ for each in cast(Any, annotation[PdfAttributes.AP])[PdfAttributes.N]:
378
+ if (value and str(each) != PdfAttributes.Off) or (
379
+ not value and str(each) == PdfAttributes.Off
380
+ ):
381
+ annotation[NameObject(PdfAttributes.AS)] = NameObject(each)
382
+ annotation[NameObject(PdfAttributes.V)] = NameObject(each)
383
+ return
384
+
385
+ if PdfAttributes.V in annotation:
386
+ del annotation[PdfAttributes.V]
387
+ if PdfAttributes.AS in annotation:
388
+ del annotation[PdfAttributes.AS]
389
+
390
+ @staticmethod
391
+ def _fill_radio(annotation: DictionaryObject, widget: RadioWidget) -> None:
392
+
393
+ for each in (
394
+ cast(Any, annotation[PdfAttributes.Kids])
395
+ if PdfAttributes.T in annotation
396
+ else cast(Any, annotation[PdfAttributes.Parent])[PdfAttributes.Kids]
397
+ ):
398
+ # determine the export value of each kid
399
+ keys = each[PdfAttributes.AP][PdfAttributes.N].keys()
400
+ if PdfAttributes.Off in keys:
401
+ keys.remove(PdfAttributes.Off)
402
+ export = list(keys)[0] or None
403
+
404
+ if f"/{widget.value}" == export:
405
+ annotation[NameObject(PdfAttributes.AS)] = NameObject(export)
406
+ cast(Any, annotation[NameObject(PdfAttributes.Parent)])[
407
+ NameObject(PdfAttributes.V)
408
+ ] = NameObject(export)
409
+ else:
410
+ annotation[NameObject(PdfAttributes.AS)] = NameObject(PdfAttributes.Off)
411
+
412
+ @staticmethod
413
+ def flatten_radio(annot: DictionaryObject, val: bool) -> None:
414
+ """
415
+ Flattens a radio button annotation by setting or unsetting the ReadOnly flag,
416
+ making it non-editable or editable based on the `val` parameter.
417
+
418
+ This function modifies the Ff (flags) entry in the radio button's annotation
419
+ dictionary or its parent dictionary if `Parent` exists in `annot`, to set or
420
+ unset the ReadOnly flag, preventing or allowing the user from changing the
421
+ selected option.
422
+
423
+ Args:
424
+ annot (DictionaryObject): The radio button annotation dictionary.
425
+ val (bool): True to flatten (make read-only), False to unflatten (make editable).
426
+ """
427
+ if PdfAttributes.Parent in annot:
428
+ cast(Any, annot[NameObject(PdfAttributes.Parent)])[NameObject(PdfAttributes.Ff)] = (
429
+ NumberObject(
430
+ (
431
+ int(
432
+ cast(Any, annot[NameObject(PdfAttributes.Parent)]).get(
433
+ NameObject(PdfAttributes.Ff), 0
434
+ )
435
+ )
436
+ | PdfAttributes.READ_ONLY
437
+ if val
438
+ else int(
439
+ cast(Any, annot[NameObject(PdfAttributes.Parent)]).get(
440
+ NameObject(PdfAttributes.Ff), 0
441
+ )
442
+ )
443
+ & ~PdfAttributes.READ_ONLY
444
+ )
445
+ )
446
+ )
447
+ else:
448
+ annot[NameObject(PdfAttributes.Ff)] = NumberObject(
449
+ (
450
+ int(annot.get(NameObject(PdfAttributes.Ff), 0)) | PdfAttributes.READ_ONLY
451
+ if val
452
+ else int(annot.get(NameObject(PdfAttributes.Ff), 0)) & ~PdfAttributes.READ_ONLY
453
+ )
454
+ )
455
+
456
+ @staticmethod
457
+ def flatten_generic(annot: DictionaryObject, val: bool) -> None:
458
+ """
459
+ Flattens a generic annotation by setting or unsetting the ReadOnly flag,
460
+ making it non-editable or editable based on the `val` parameter.
461
+
462
+ This function modifies the Ff (flags) entry in the annotation dictionary to
463
+ set or unset the ReadOnly flag, preventing or allowing the user from
464
+ interacting with the form field.
465
+
466
+ Args:
467
+ annot (DictionaryObject): The annotation dictionary.
468
+ val (bool): True to flatten (make read-only), False to unflatten (make editable).
469
+ """
470
+ if PdfAttributes.Parent in annot and PdfAttributes.Ff not in annot:
471
+ cast(Any, annot[NameObject(PdfAttributes.Parent)])[NameObject(PdfAttributes.Ff)] = (
472
+ NumberObject(
473
+ (
474
+ int(annot.get(NameObject(PdfAttributes.Ff), 0)) | PdfAttributes.READ_ONLY
475
+ if val
476
+ else int(annot.get(NameObject(PdfAttributes.Ff), 0))
477
+ & ~PdfAttributes.READ_ONLY
478
+ )
479
+ )
480
+ )
481
+ else:
482
+ annot[NameObject(PdfAttributes.Ff)] = NumberObject(
483
+ (
484
+ int(annot.get(NameObject(PdfAttributes.Ff), 0)) | PdfAttributes.READ_ONLY
485
+ if val
486
+ else int(annot.get(NameObject(PdfAttributes.Ff), 0)) & ~PdfAttributes.READ_ONLY
487
+ )
488
+ )
pdffiller/py.typed.py ADDED
File without changes
pdffiller/typing.py ADDED
@@ -0,0 +1,59 @@
1
+ import argparse
2
+ from pathlib import Path
3
+ from typing import (
4
+ Any,
5
+ Callable,
6
+ cast,
7
+ Dict,
8
+ Generator,
9
+ IO,
10
+ ItemsView,
11
+ List,
12
+ Mapping,
13
+ Optional,
14
+ overload,
15
+ Sequence,
16
+ Tuple,
17
+ Type,
18
+ TYPE_CHECKING,
19
+ TypedDict,
20
+ TypeVar,
21
+ Union,
22
+ )
23
+
24
+ __all__ = [
25
+ "cast",
26
+ "overload",
27
+ "Any",
28
+ "Callable",
29
+ "Dict",
30
+ "ExitCode",
31
+ "Generator",
32
+ "IO",
33
+ "ItemsView",
34
+ "List",
35
+ "Optional",
36
+ "Mapping",
37
+ "PathLike",
38
+ "Sequence",
39
+ "Tuple",
40
+ "TypedDict",
41
+ "TypeVar",
42
+ "Type",
43
+ "Union",
44
+ "SubParserType",
45
+ "StrByteType",
46
+ ]
47
+
48
+
49
+ ExitCode = Union[str, int, None]
50
+ PathLike = Union[str, Path]
51
+
52
+ StreamType = IO[Any]
53
+ StrByteType = Union[PathLike, StreamType]
54
+
55
+ if TYPE_CHECKING:
56
+ # pylint: disable=protected-access,line-too-long
57
+ SubParserType = argparse._SubParsersAction["PdfFillerStoreArgumentParser"] # type: ignore[name-defined]
58
+ else:
59
+ SubParserType = Any
pdffiller/utils.py ADDED
@@ -0,0 +1,36 @@
1
+ import os
2
+ from pathlib import Path
3
+
4
+ from pdffiller.typing import Any, Optional, PathLike
5
+
6
+
7
+ def str_to_path(path: Optional[PathLike]) -> Any:
8
+ """Convert string or Path to Path
9
+
10
+ :param path: The path to be converted
11
+ :return: The converted path into Path object if successful, else None
12
+ """
13
+ if not path:
14
+ return None
15
+
16
+ if not isinstance(path, Path):
17
+ try:
18
+ new_path = Path(str(path))
19
+ except RuntimeError:
20
+ new_path = None
21
+ else:
22
+ new_path = path
23
+
24
+ return new_path
25
+
26
+
27
+ def path_to_str(path: Optional[PathLike]) -> Any:
28
+ """Convert string or path to string only
29
+
30
+ :param path: The path to be converted
31
+ :return: The converted string from ``path`` if successful, else None
32
+ """
33
+ if not path:
34
+ return None
35
+
36
+ return os.fspath(path)
File without changes
@@ -0,0 +1,107 @@
1
+ """
2
+ This module defines the base class for all widgets in PyPdfFormFiller.
3
+
4
+ This module defines the base class for form widgets, which are used to
5
+ represent form fields in a PDF document. The Widget class provides
6
+ common attributes and methods for all form widgets, such as name, value,
7
+ and schema definition.
8
+ """
9
+
10
+ from typing import Any, Dict, Optional
11
+
12
+
13
+ class Widget:
14
+ """
15
+ Base class for form widget.
16
+
17
+ The Widget class provides a base implementation for form widgets,
18
+ which are used to represent form fields in a PDF document. It
19
+ defines common attributes and methods for all form widgets, such
20
+ as name, value, and schema definition.
21
+ """
22
+
23
+ def __init__(self, name: str, page_number: int, value: Optional[Any] = None) -> None:
24
+ """
25
+ Initialize a new widget.
26
+
27
+ Args:
28
+ name (str): The name of the widget.
29
+ page_number (int): The associated page index
30
+ value (Any): The initial value of the widget. Defaults to None.
31
+ """
32
+ super().__init__()
33
+ self._name: str = name
34
+ self.page_number: int = page_number
35
+ self._value: Optional[Any] = value
36
+ self._description: Optional[str] = None
37
+
38
+ @property
39
+ def name(self) -> str:
40
+ """
41
+ Get the name of the widget.
42
+
43
+ Returns:
44
+ str: The name of the widget.
45
+ """
46
+ return self._name
47
+
48
+ @property
49
+ def value(self) -> Any:
50
+ """
51
+ Get the value of the widget.
52
+
53
+ Returns:
54
+ Any: The value of the widget.
55
+ """
56
+ return self._value
57
+
58
+ @value.setter
59
+ def value(self, value: Any) -> None:
60
+ """
61
+ Set the value of the widget.
62
+
63
+ Args:
64
+ value (Any): The value to set.
65
+ """
66
+ self._value = value
67
+
68
+ @property
69
+ def description(self) -> Optional[str]:
70
+ """
71
+ Get the description of the widget.
72
+
73
+ Returns:
74
+ Any: The description of the widget.
75
+ """
76
+ return self._description
77
+
78
+ @description.setter
79
+ def description(self, description: Optional[str]) -> None:
80
+ """
81
+ Set the description of the widget.
82
+
83
+ Args:
84
+ description (str): The description to set.
85
+ """
86
+ self._description = description
87
+
88
+ @property
89
+ def schema_definition(self) -> Dict[str, Any]:
90
+ """
91
+ Get the schema definition of the widget.
92
+
93
+ This method returns a dictionary that defines the schema
94
+ for the widget. The schema definition is used to validate
95
+ the widget's value.
96
+
97
+ Returns:
98
+ dict: The schema definition of the widget.
99
+ """
100
+ result: Dict[str, Any] = {"FieldName": self._name}
101
+ if self._value:
102
+ result["FieldValue"] = self._value
103
+
104
+ if self._description is not None:
105
+ result["Description"] = self._description
106
+
107
+ return result