1# Copyright (c) 2010-2024 openpyxl
2
3from openpyxl.compat import safe_string
4from openpyxl.xml.functions import Element, SubElement, whitespace, XML_NS
5from openpyxl import LXML
6from openpyxl.utils.datetime import to_excel, to_ISO8601
7from datetime import timedelta
8
9from openpyxl.worksheet.formula import DataTableFormula, ArrayFormula
10from openpyxl.cell.rich_text import CellRichText
11
12def _set_attributes(cell, styled=None):
13 """
14 Set coordinate and datatype
15 """
16 coordinate = cell.coordinate
17 attrs = {'r': coordinate}
18 if styled:
19 attrs['s'] = f"{cell.style_id}"
20
21 if cell.data_type == "s":
22 attrs['t'] = "inlineStr"
23 elif cell.data_type != 'f':
24 attrs['t'] = cell.data_type
25
26 value = cell._value
27
28 if cell.data_type == "d":
29 if hasattr(value, "tzinfo") and value.tzinfo is not None:
30 raise TypeError("Excel does not support timezones in datetimes. "
31 "The tzinfo in the datetime/time object must be set to None.")
32
33 if cell.parent.parent.iso_dates and not isinstance(value, timedelta):
34 value = to_ISO8601(value)
35 else:
36 attrs['t'] = "n"
37 value = to_excel(value, cell.parent.parent.epoch)
38
39 if cell.hyperlink:
40 cell.parent._hyperlinks.append(cell.hyperlink)
41
42 return value, attrs
43
44
45def etree_write_cell(xf, worksheet, cell, styled=None):
46
47 value, attributes = _set_attributes(cell, styled)
48
49 el = Element("c", attributes)
50 if value is None or value == "":
51 xf.write(el)
52 return
53
54 if cell.data_type == 'f':
55 attrib = {}
56
57 if isinstance(value, ArrayFormula):
58 attrib = dict(value)
59 value = value.text
60
61 elif isinstance(value, DataTableFormula):
62 attrib = dict(value)
63 value = None
64
65 formula = SubElement(el, 'f', attrib)
66 if value is not None and not attrib.get('t') == "dataTable":
67 formula.text = value[1:]
68 value = None
69
70 if cell.data_type == 's':
71 if isinstance(value, CellRichText):
72 el.append(value.to_tree())
73 else:
74 inline_string = Element("is")
75 text = Element('t')
76 text.text = value
77 whitespace(text)
78 inline_string.append(text)
79 el.append(inline_string)
80
81 else:
82 cell_content = SubElement(el, 'v')
83 if value is not None:
84 cell_content.text = safe_string(value)
85
86 xf.write(el)
87
88
89def lxml_write_cell(xf, worksheet, cell, styled=False):
90 value, attributes = _set_attributes(cell, styled)
91
92 if value == '' or value is None:
93 with xf.element("c", attributes):
94 return
95
96 with xf.element('c', attributes):
97 if cell.data_type == 'f':
98 attrib = {}
99
100 if isinstance(value, ArrayFormula):
101 attrib = dict(value)
102 value = value.text
103
104 elif isinstance(value, DataTableFormula):
105 attrib = dict(value)
106 value = None
107
108 with xf.element('f', attrib):
109 if value is not None and not attrib.get('t') == "dataTable":
110 xf.write(value[1:])
111 value = None
112
113 if cell.data_type == 's':
114 if isinstance(value, CellRichText):
115 el = value.to_tree()
116 xf.write(el)
117 else:
118 with xf.element("is"):
119 if isinstance(value, str):
120 attrs = {}
121 if value != value.strip():
122 attrs["{%s}space" % XML_NS] = "preserve"
123 el = Element("t", attrs) # lxml can't handle xml-ns
124 el.text = value
125 xf.write(el)
126
127 else:
128 with xf.element("v"):
129 if value is not None:
130 xf.write(safe_string(value))
131
132
133if LXML:
134 write_cell = lxml_write_cell
135else:
136 write_cell = etree_write_cell