Skip to content

Element Typed

ElementTyped subclass of Element.

Classes:

Name Description
ElementTyped

Subclass of Element for classes managing typed values.

ElementTyped

Bases: Element

Subclass of Element for classes managing typed values.

Methods:

Name Description
clear_attrinutes

Clear attrinutes defining type and value of the Cell.

delete_children

Delete all children of the ElementType.

get_value

Get the Python-typed value of the element.

set_text_content

Sets the text content of the embedded paragraph.

set_value_and_type

Set the value and type of the element.

Source code in odfdo/element_typed.py
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
class ElementTyped(Element):
    """Subclass of Element for classes managing typed values."""

    def _erase_text_content(self) -> None:
        """Clear the textual content of the element.

        This method removes all paragraph elements (`text:p`), effectively
        erasing any visible text within the element.
        """
        paragraphs = self.get_elements("text:p")
        if not paragraphs:
            # E.g., text:p in draw:text-box in draw:frame
            paragraphs = self.get_elements("*/text:p")
        if paragraphs:
            # paragraphs.pop(0)
            for obsolete in paragraphs:
                obsolete.delete()

    def set_text_content(self, text: str | Element | None) -> None:
        """Sets the text content of the embedded paragraph.

        This operation overwrites all existing text nodes and children
        that may contain text.

        Args:
            text: The new text content.
        """
        self.delete_children()
        self.text_content = text

    def clear_attrinutes(self) -> None:
        """Clear attrinutes defining type and value of the Cell."""
        self.del_attribute_list(
            (
                "office:value-type",
                "office:boolean-value",
                "office:value",
                "office:date-value",
                "office:string-value",
                "office:time-value",
                "office:currency",
                "calcext:value-type",
                "calcext:value",
                "loext:value-type",
                "loext:active-table",
                "loext:location",
                "loext:target",
            )
        )

    def delete_children(self) -> None:
        """Delete all children of the ElementType."""
        for child in self.children:
            self.delete(child, keep_tail=False)

    def set_value_and_type(
        self,
        value: CellValue | None,
        value_type: str | None = None,
        text: str | None = None,
        currency: str | None = None,
    ) -> str | None:
        """Set the value and type of the element.

        This method handles setting the appropriate ODF attributes
        (e.g., `office:value`, `office:value-type`, `office:string-value`)
        based on the Python type of the provided `value`.

        Args:
            value: The value to set.
            value_type: The ODF value type (e.g., "float", "date", "string").
                If not provided, it is inferred from the type of `value`.
            text: The textual representation of the value. If not provided,
                it is generated automatically.
            currency: The currency symbol, used when `value_type` is
                "currency".

        Returns:
            str | None: The textual representation of the value that was set,
                or `None` if the value was `None`.

        Raises:
            TypeError: If the type of `value` is not supported.
        """
        # Remove possible previous value and type
        self.clear_attrinutes()
        if isinstance(value, bytes):
            value = bytes_to_str(value)
        if isinstance(value_type, bytes):
            value_type = bytes_to_str(value_type)
        if isinstance(text, bytes):
            text = bytes_to_str(text)
        if isinstance(currency, bytes):
            currency = bytes_to_str(currency)
        if value is None:
            self._erase_text_content()
            return text
        if isinstance(value, bool):
            if value_type is None:
                value_type = "boolean"
            if text is None:
                text = "true" if value else "false"
            value = Boolean.encode(value)
        elif isinstance(value, (int, float, Decimal)):
            if value_type == "percentage":
                text = f"{int(value * 100)} %"
            if value_type is None:
                value_type = "float"
            if value_type == "boolean":
                if text is None:
                    text = "true" if bool(value) else "false"
                value = Boolean.encode(value)
            else:
                f_val = float(value)
                if math.isnan(f_val):
                    val_str = "NaN"
                elif math.isinf(f_val):
                    val_str = "INF" if f_val > 0 else "-INF"
                else:
                    val_str = str(value)
                if text is None:
                    text = val_str
                value = val_str
        elif isinstance(value, datetime):
            if value_type is None:
                value_type = "date"
            if text is None:
                text = str(DateTime.encode(value))
            value = DateTime.encode(value)
        elif isinstance(value, date):
            if value_type is None:
                value_type = "date"
            if text is None:
                text = str(Date.encode(value))
            value = Date.encode(value)
        elif isinstance(value, str):
            if value_type is None:
                value_type = "string"
            if text is None:
                text = value
        elif isinstance(value, timedelta):
            if value_type is None:
                value_type = "time"
            if text is None:
                text = str(Duration.encode(value))
            value = Duration.encode(value)
        else:
            raise TypeError(f"Type unknown: '{value!r}'")

        if isinstance(value_type, str):
            self.set_attribute("office:value-type", value_type)
            self.set_attribute("calcext:value-type", value_type)
        if value_type == "boolean":
            self.set_attribute("office:boolean-value", value)
        elif value_type == "currency":
            self.set_attribute("office:value", value)
            self.set_attribute("office:currency", currency)
        elif value_type == "date":
            self.set_attribute("office:date-value", value)
        elif value_type in ("float", "percentage"):
            self.set_attribute("office:value", value)
            self.set_attribute("calcext:value", value)
        elif value_type == "string":
            self.set_attribute("office:string-value", value)
        elif value_type == "time":
            self.set_attribute("office:time-value", value)

        return text

    def _get_typed_value_boolean(self) -> bool:
        """Get the boolean value from the 'office:boolean-value' attribute."""
        return Boolean.decode(self.get_attribute("office:boolean-value"))

    def _get_typed_value_number_type(self) -> Decimal | int | float:
        """Get the numeric value from the 'office:value' attribute.

        Returns the value as an `int` if possible, otherwise as a `Decimal`.
        """
        read_number = self.get_attribute_string("office:value")
        if read_number is None:
            raise ValueError('"office:value" has None value')
        value = Decimal(read_number)
        # Return 3 instead of 3.0 if possible
        if not value.is_nan() and not value.is_infinite():
            with contextlib.suppress(ValueError, OverflowError):
                if int(value) == value:
                    return int(value)
        return value

    def _get_typed_value_float(self) -> Decimal | int | float:
        """Get the float value, delegating to _get_typed_value_number_type."""
        return self._get_typed_value_number_type()

    def _get_typed_value_percentage(self) -> Decimal | int | float:
        """Get the percentage value, delegating to _get_typed_value_number_type."""
        return self._get_typed_value_number_type()

    def _get_typed_value_currency(self) -> Decimal | int | float:
        """Get the currency value, delegating to _get_typed_value_number_type."""
        return self._get_typed_value_number_type()

    def _get_typed_value_date(self) -> date | datetime:
        """Get the date or datetime value from the 'office:date-value' attribute."""
        read_attribute = self.get_attribute_string("office:date-value")
        if read_attribute is None:
            raise ValueError('"office:date-value" has None value')
        if "T" in read_attribute:
            return DateTime.decode(read_attribute)
        return Date.decode(read_attribute)

    def _get_typed_value_string(self, try_get_text: bool) -> str | None:
        """Get the string value.

        Tries to get the value from the 'office:string-value' attribute first.
        If that fails, and `try_get_text` is True, it attempts to get the
        text content from child paragraph elements.
        """
        value = self.get_attribute_string("office:string-value")
        if value is not None:
            return str(value)
        if try_get_text:
            list_value = [para.inner_text for para in self.get_elements("text:p")]
            if list_value:
                return "\n".join(list_value)
        return None

    def _get_typed_value_time(self) -> timedelta:
        """Get the time value from the 'office:time-value' attribute."""
        read_value = self.get_attribute_string("office:time-value")
        if read_value is None:
            raise ValueError('"office:time-value" has None value')
        return Duration.decode(read_value)

    def _get_typed_value(
        self,
        value_type: str = "",
        try_get_text: bool = True,
    ) -> Any:
        """Get the Python-typed value based on the ODF value type.

        This is a dispatcher method that calls the appropriate `_get_typed_value_*`
        method based on the `value_type`.

        Args:
            value_type (str): The ODF value type (e.g., "string", "float").
            try_get_text (bool): For string types, whether to fall back to
                reading text from child paragraphs.

        Returns:
            Any: The value converted to its corresponding Python type.

        Raises:
            TypeError: If the `value_type` is not supported.
        """
        if value_type == "string":
            return self._get_typed_value_string(try_get_text)
        method = getattr(self, f"_get_typed_value_{value_type}", None)
        if method is None:
            raise TypeError(f"Unexpected value type: {value_type}")
        return method()

    def _get_value_and_type(
        self, value_type: str | None = None, try_get_text: bool = True
    ) -> tuple[CellValue | None, str | None]:
        """Get the value and its ODF type.

        If `value_type` is not provided, it is read from the element's
        'office:value-type' attribute.

        Args:
            value_type (str, optional): The expected ODF value type.
            try_get_text (bool): For string types, whether to fall back to
                reading text from child paragraphs.

        Returns:
            tuple[Any, str | None]: A tuple containing the Python-typed value
                and the ODF value type string, or (None, None) if the type
                cannot be determined.
        """
        if value_type is None:
            read_value_type = self.get_attribute_string("office:value-type")
            if read_value_type is None:
                return None, None
            value_type = read_value_type
        return (
            self._get_typed_value(
                value_type=value_type,
                try_get_text=try_get_text,
            ),
            value_type,
        )

    def get_value(
        self,
        value_type: str | None = None,
        try_get_text: bool = True,
        get_type: bool = False,
    ) -> CellValue | tuple[CellValue | None, str | None] | None:
        """Get the Python-typed value of the element.

        This method is for elements that have an `office:value-type`
        attribute. It does not apply to meta fields.

        Args:
            value_type: The expected ODF value type. If not provided, it's
                inferred from the 'office:value-type' attribute.
            try_get_text: For string types, whether to fall back to
                reading text from child paragraphs.
            get_type: If True, returns a tuple of (value, type_string)
                instead of just the value.

        Returns:
            The Python-typed value, or a tuple of (value, type_string) if
            `get_type` is True.
        """
        value, actual_type = self._get_value_and_type(
            value_type=value_type, try_get_text=try_get_text
        )
        if get_type:
            return (value, actual_type)
        return value

_erase_text_content

_erase_text_content() -> None

Clear the textual content of the element.

This method removes all paragraph elements (text:p), effectively erasing any visible text within the element.

Source code in odfdo/element_typed.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
def _erase_text_content(self) -> None:
    """Clear the textual content of the element.

    This method removes all paragraph elements (`text:p`), effectively
    erasing any visible text within the element.
    """
    paragraphs = self.get_elements("text:p")
    if not paragraphs:
        # E.g., text:p in draw:text-box in draw:frame
        paragraphs = self.get_elements("*/text:p")
    if paragraphs:
        # paragraphs.pop(0)
        for obsolete in paragraphs:
            obsolete.delete()

_get_typed_value

_get_typed_value(
    value_type: str = "", try_get_text: bool = True
) -> Any

Get the Python-typed value based on the ODF value type.

This is a dispatcher method that calls the appropriate _get_typed_value_* method based on the value_type.

Parameters:

Name Type Description Default
value_type str

The ODF value type (e.g., “string”, “float”).

''
try_get_text bool

For string types, whether to fall back to reading text from child paragraphs.

True

Returns:

Name Type Description
Any Any

The value converted to its corresponding Python type.

Raises:

Type Description
TypeError

If the value_type is not supported.

Source code in odfdo/element_typed.py
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
def _get_typed_value(
    self,
    value_type: str = "",
    try_get_text: bool = True,
) -> Any:
    """Get the Python-typed value based on the ODF value type.

    This is a dispatcher method that calls the appropriate `_get_typed_value_*`
    method based on the `value_type`.

    Args:
        value_type (str): The ODF value type (e.g., "string", "float").
        try_get_text (bool): For string types, whether to fall back to
            reading text from child paragraphs.

    Returns:
        Any: The value converted to its corresponding Python type.

    Raises:
        TypeError: If the `value_type` is not supported.
    """
    if value_type == "string":
        return self._get_typed_value_string(try_get_text)
    method = getattr(self, f"_get_typed_value_{value_type}", None)
    if method is None:
        raise TypeError(f"Unexpected value type: {value_type}")
    return method()

_get_typed_value_boolean

_get_typed_value_boolean() -> bool

Get the boolean value from the ‘office:boolean-value’ attribute.

Source code in odfdo/element_typed.py
205
206
207
def _get_typed_value_boolean(self) -> bool:
    """Get the boolean value from the 'office:boolean-value' attribute."""
    return Boolean.decode(self.get_attribute("office:boolean-value"))

_get_typed_value_currency

_get_typed_value_currency() -> Decimal | int | float

Get the currency value, delegating to _get_typed_value_number_type.

Source code in odfdo/element_typed.py
233
234
235
def _get_typed_value_currency(self) -> Decimal | int | float:
    """Get the currency value, delegating to _get_typed_value_number_type."""
    return self._get_typed_value_number_type()

_get_typed_value_date

_get_typed_value_date() -> date | datetime

Get the date or datetime value from the ‘office:date-value’ attribute.

Source code in odfdo/element_typed.py
237
238
239
240
241
242
243
244
def _get_typed_value_date(self) -> date | datetime:
    """Get the date or datetime value from the 'office:date-value' attribute."""
    read_attribute = self.get_attribute_string("office:date-value")
    if read_attribute is None:
        raise ValueError('"office:date-value" has None value')
    if "T" in read_attribute:
        return DateTime.decode(read_attribute)
    return Date.decode(read_attribute)

_get_typed_value_float

_get_typed_value_float() -> Decimal | int | float

Get the float value, delegating to _get_typed_value_number_type.

Source code in odfdo/element_typed.py
225
226
227
def _get_typed_value_float(self) -> Decimal | int | float:
    """Get the float value, delegating to _get_typed_value_number_type."""
    return self._get_typed_value_number_type()

_get_typed_value_number_type

_get_typed_value_number_type() -> Decimal | int | float

Get the numeric value from the ‘office:value’ attribute.

Returns the value as an int if possible, otherwise as a Decimal.

Source code in odfdo/element_typed.py
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
def _get_typed_value_number_type(self) -> Decimal | int | float:
    """Get the numeric value from the 'office:value' attribute.

    Returns the value as an `int` if possible, otherwise as a `Decimal`.
    """
    read_number = self.get_attribute_string("office:value")
    if read_number is None:
        raise ValueError('"office:value" has None value')
    value = Decimal(read_number)
    # Return 3 instead of 3.0 if possible
    if not value.is_nan() and not value.is_infinite():
        with contextlib.suppress(ValueError, OverflowError):
            if int(value) == value:
                return int(value)
    return value

_get_typed_value_percentage

_get_typed_value_percentage() -> Decimal | int | float

Get the percentage value, delegating to _get_typed_value_number_type.

Source code in odfdo/element_typed.py
229
230
231
def _get_typed_value_percentage(self) -> Decimal | int | float:
    """Get the percentage value, delegating to _get_typed_value_number_type."""
    return self._get_typed_value_number_type()

_get_typed_value_string

_get_typed_value_string(try_get_text: bool) -> str | None

Get the string value.

Tries to get the value from the ‘office:string-value’ attribute first. If that fails, and try_get_text is True, it attempts to get the text content from child paragraph elements.

Source code in odfdo/element_typed.py
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
def _get_typed_value_string(self, try_get_text: bool) -> str | None:
    """Get the string value.

    Tries to get the value from the 'office:string-value' attribute first.
    If that fails, and `try_get_text` is True, it attempts to get the
    text content from child paragraph elements.
    """
    value = self.get_attribute_string("office:string-value")
    if value is not None:
        return str(value)
    if try_get_text:
        list_value = [para.inner_text for para in self.get_elements("text:p")]
        if list_value:
            return "\n".join(list_value)
    return None

_get_typed_value_time

_get_typed_value_time() -> timedelta

Get the time value from the ‘office:time-value’ attribute.

Source code in odfdo/element_typed.py
262
263
264
265
266
267
def _get_typed_value_time(self) -> timedelta:
    """Get the time value from the 'office:time-value' attribute."""
    read_value = self.get_attribute_string("office:time-value")
    if read_value is None:
        raise ValueError('"office:time-value" has None value')
    return Duration.decode(read_value)

_get_value_and_type

_get_value_and_type(
    value_type: str | None = None, try_get_text: bool = True
) -> tuple[CellValue | None, str | None]

Get the value and its ODF type.

If value_type is not provided, it is read from the element’s ‘office:value-type’ attribute.

Parameters:

Name Type Description Default
value_type str

The expected ODF value type.

None
try_get_text bool

For string types, whether to fall back to reading text from child paragraphs.

True

Returns:

Type Description
tuple[CellValue | None, str | None]

tuple[Any, str | None]: A tuple containing the Python-typed value and the ODF value type string, or (None, None) if the type cannot be determined.

Source code in odfdo/element_typed.py
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
def _get_value_and_type(
    self, value_type: str | None = None, try_get_text: bool = True
) -> tuple[CellValue | None, str | None]:
    """Get the value and its ODF type.

    If `value_type` is not provided, it is read from the element's
    'office:value-type' attribute.

    Args:
        value_type (str, optional): The expected ODF value type.
        try_get_text (bool): For string types, whether to fall back to
            reading text from child paragraphs.

    Returns:
        tuple[Any, str | None]: A tuple containing the Python-typed value
            and the ODF value type string, or (None, None) if the type
            cannot be determined.
    """
    if value_type is None:
        read_value_type = self.get_attribute_string("office:value-type")
        if read_value_type is None:
            return None, None
        value_type = read_value_type
    return (
        self._get_typed_value(
            value_type=value_type,
            try_get_text=try_get_text,
        ),
        value_type,
    )

clear_attrinutes

clear_attrinutes() -> None

Clear attrinutes defining type and value of the Cell.

Source code in odfdo/element_typed.py
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def clear_attrinutes(self) -> None:
    """Clear attrinutes defining type and value of the Cell."""
    self.del_attribute_list(
        (
            "office:value-type",
            "office:boolean-value",
            "office:value",
            "office:date-value",
            "office:string-value",
            "office:time-value",
            "office:currency",
            "calcext:value-type",
            "calcext:value",
            "loext:value-type",
            "loext:active-table",
            "loext:location",
            "loext:target",
        )
    )

delete_children

delete_children() -> None

Delete all children of the ElementType.

Source code in odfdo/element_typed.py
86
87
88
89
def delete_children(self) -> None:
    """Delete all children of the ElementType."""
    for child in self.children:
        self.delete(child, keep_tail=False)

get_value

get_value(
    value_type: str | None = None,
    try_get_text: bool = True,
    get_type: bool = False,
) -> CellValue | tuple[CellValue | None, str | None] | None

Get the Python-typed value of the element.

This method is for elements that have an office:value-type attribute. It does not apply to meta fields.

Parameters:

Name Type Description Default
value_type str | None

The expected ODF value type. If not provided, it’s inferred from the ‘office:value-type’ attribute.

None
try_get_text bool

For string types, whether to fall back to reading text from child paragraphs.

True
get_type bool

If True, returns a tuple of (value, type_string) instead of just the value.

False

Returns:

Type Description
CellValue | tuple[CellValue | None, str | None] | None

The Python-typed value, or a tuple of (value, type_string) if

CellValue | tuple[CellValue | None, str | None] | None

get_type is True.

Source code in odfdo/element_typed.py
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
def get_value(
    self,
    value_type: str | None = None,
    try_get_text: bool = True,
    get_type: bool = False,
) -> CellValue | tuple[CellValue | None, str | None] | None:
    """Get the Python-typed value of the element.

    This method is for elements that have an `office:value-type`
    attribute. It does not apply to meta fields.

    Args:
        value_type: The expected ODF value type. If not provided, it's
            inferred from the 'office:value-type' attribute.
        try_get_text: For string types, whether to fall back to
            reading text from child paragraphs.
        get_type: If True, returns a tuple of (value, type_string)
            instead of just the value.

    Returns:
        The Python-typed value, or a tuple of (value, type_string) if
        `get_type` is True.
    """
    value, actual_type = self._get_value_and_type(
        value_type=value_type, try_get_text=try_get_text
    )
    if get_type:
        return (value, actual_type)
    return value

set_text_content

set_text_content(text: str | Element | None) -> None

Sets the text content of the embedded paragraph.

This operation overwrites all existing text nodes and children that may contain text.

Parameters:

Name Type Description Default
text str | Element | None

The new text content.

required
Source code in odfdo/element_typed.py
54
55
56
57
58
59
60
61
62
63
64
def set_text_content(self, text: str | Element | None) -> None:
    """Sets the text content of the embedded paragraph.

    This operation overwrites all existing text nodes and children
    that may contain text.

    Args:
        text: The new text content.
    """
    self.delete_children()
    self.text_content = text

set_value_and_type

set_value_and_type(
    value: CellValue | None,
    value_type: str | None = None,
    text: str | None = None,
    currency: str | None = None,
) -> str | None

Set the value and type of the element.

This method handles setting the appropriate ODF attributes (e.g., office:value, office:value-type, office:string-value) based on the Python type of the provided value.

Parameters:

Name Type Description Default
value CellValue | None

The value to set.

required
value_type str | None

The ODF value type (e.g., “float”, “date”, “string”). If not provided, it is inferred from the type of value.

None
text str | None

The textual representation of the value. If not provided, it is generated automatically.

None
currency str | None

The currency symbol, used when value_type is “currency”.

None

Returns:

Type Description
str | None

str | None: The textual representation of the value that was set, or None if the value was None.

Raises:

Type Description
TypeError

If the type of value is not supported.

Source code in odfdo/element_typed.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
def set_value_and_type(
    self,
    value: CellValue | None,
    value_type: str | None = None,
    text: str | None = None,
    currency: str | None = None,
) -> str | None:
    """Set the value and type of the element.

    This method handles setting the appropriate ODF attributes
    (e.g., `office:value`, `office:value-type`, `office:string-value`)
    based on the Python type of the provided `value`.

    Args:
        value: The value to set.
        value_type: The ODF value type (e.g., "float", "date", "string").
            If not provided, it is inferred from the type of `value`.
        text: The textual representation of the value. If not provided,
            it is generated automatically.
        currency: The currency symbol, used when `value_type` is
            "currency".

    Returns:
        str | None: The textual representation of the value that was set,
            or `None` if the value was `None`.

    Raises:
        TypeError: If the type of `value` is not supported.
    """
    # Remove possible previous value and type
    self.clear_attrinutes()
    if isinstance(value, bytes):
        value = bytes_to_str(value)
    if isinstance(value_type, bytes):
        value_type = bytes_to_str(value_type)
    if isinstance(text, bytes):
        text = bytes_to_str(text)
    if isinstance(currency, bytes):
        currency = bytes_to_str(currency)
    if value is None:
        self._erase_text_content()
        return text
    if isinstance(value, bool):
        if value_type is None:
            value_type = "boolean"
        if text is None:
            text = "true" if value else "false"
        value = Boolean.encode(value)
    elif isinstance(value, (int, float, Decimal)):
        if value_type == "percentage":
            text = f"{int(value * 100)} %"
        if value_type is None:
            value_type = "float"
        if value_type == "boolean":
            if text is None:
                text = "true" if bool(value) else "false"
            value = Boolean.encode(value)
        else:
            f_val = float(value)
            if math.isnan(f_val):
                val_str = "NaN"
            elif math.isinf(f_val):
                val_str = "INF" if f_val > 0 else "-INF"
            else:
                val_str = str(value)
            if text is None:
                text = val_str
            value = val_str
    elif isinstance(value, datetime):
        if value_type is None:
            value_type = "date"
        if text is None:
            text = str(DateTime.encode(value))
        value = DateTime.encode(value)
    elif isinstance(value, date):
        if value_type is None:
            value_type = "date"
        if text is None:
            text = str(Date.encode(value))
        value = Date.encode(value)
    elif isinstance(value, str):
        if value_type is None:
            value_type = "string"
        if text is None:
            text = value
    elif isinstance(value, timedelta):
        if value_type is None:
            value_type = "time"
        if text is None:
            text = str(Duration.encode(value))
        value = Duration.encode(value)
    else:
        raise TypeError(f"Type unknown: '{value!r}'")

    if isinstance(value_type, str):
        self.set_attribute("office:value-type", value_type)
        self.set_attribute("calcext:value-type", value_type)
    if value_type == "boolean":
        self.set_attribute("office:boolean-value", value)
    elif value_type == "currency":
        self.set_attribute("office:value", value)
        self.set_attribute("office:currency", currency)
    elif value_type == "date":
        self.set_attribute("office:date-value", value)
    elif value_type in ("float", "percentage"):
        self.set_attribute("office:value", value)
        self.set_attribute("calcext:value", value)
    elif value_type == "string":
        self.set_attribute("office:string-value", value)
    elif value_type == "time":
        self.set_attribute("office:time-value", value)

    return text