Skip to content

Commit 3f3659b

Browse files
Implement styling
1 parent 955c7bb commit 3f3659b

File tree

1 file changed

+41
-84
lines changed

1 file changed

+41
-84
lines changed

pandas/io/excel/_odswriter.py

Lines changed: 41 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
from collections import defaultdict
22
import datetime
33

4-
# import pandas._libs.json as json
4+
import pandas._libs.json as json
55

66
from pandas.io.excel._base import ExcelWriter
77

88
# from pandas.io.excel._util import _validate_freeze_panes
99

1010
from odf.opendocument import OpenDocumentSpreadsheet
11+
from odf.style import Style, TextProperties, TableCellProperties, ParagraphProperties
1112
from odf.table import Table, TableRow, TableCell
1213
from odf.text import P
1314

@@ -24,12 +25,8 @@ def __init__(self, path, engine=None, encoding=None, mode="w", **engine_kwargs):
2425

2526
super().__init__(path, mode=mode, **engine_kwargs)
2627

27-
if encoding is None:
28-
encoding = "ascii"
2928
self.book = OpenDocumentSpreadsheet()
30-
31-
# self.fm_datetime = xlwt.easyxf(num_format_str=self.datetime_format)
32-
# self.fm_date = xlwt.easyxf(num_format_str=self.date_format)
29+
self.style_dict = {}
3330

3431
def save(self):
3532
"""
@@ -57,25 +54,25 @@ def write_cells(
5754
# wks.set_horz_split_pos(freeze_panes[0])
5855
# wks.set_vert_split_pos(freeze_panes[1])
5956

60-
style_dict = {}
6157

6258
rows = defaultdict(TableRow)
6359
col_count = defaultdict(int)
6460

6561
for cell in sorted(cells, key=lambda cell: (cell.row, cell.col)):
6662
attributes = {}
63+
style_name = self._process_style(cell.style)
64+
if style_name is not None:
65+
attributes["stylename"] = style_name
6766
print(cell.row, cell.col, cell.val, cell.mergestart, cell.mergeend)
6867
if cell.mergestart is not None and cell.mergeend is not None:
69-
attributes = {
70-
"numberrowsspanned": max(1, cell.mergestart),
71-
"numbercolumnsspanned": cell.mergeend,
72-
}
68+
attributes["numberrowsspanned"] = max(1, cell.mergestart)
69+
attributes["numbercolumnsspanned"] = cell.mergeend
7370
# fill with empty cells if needed
7471
for _ in range(cell.col - col_count[cell.row]):
7572
rows[cell.row].addElement(TableCell())
7673
col_count[cell.row] += 1
7774
val, fmt = self._value_with_fmt(cell.val)
78-
print("type", type(val), "value", val)
75+
# print("type", type(val), "value", val)
7976
pvalue = value = val
8077
if isinstance(val, bool):
8178
value = str(val).lower()
@@ -108,78 +105,38 @@ def write_cells(
108105
col_count[cell.row] += 1
109106
p = P(text=pvalue)
110107
tc.addElement(p)
111-
"""
112-
stylekey = json.dumps(cell.style)
113-
if fmt:
114-
stylekey += fmt
115-
116-
if stylekey in style_dict:
117-
style = style_dict[stylekey]
118-
else:
119-
style = self._convert_to_style(cell.style, fmt)
120-
style_dict[stylekey] = style
121-
"""
122108
for row_nr in range(max(rows.keys()) + 1):
123109
wks.addElement(rows[row_nr])
124110

125-
@classmethod
126-
def _style_to_xlwt(
127-
cls, item, firstlevel: bool = True, field_sep=",", line_sep=";"
128-
) -> str:
129-
"""
130-
helper which recursively generate an xlwt easy style string
131-
for example:
132-
133-
hstyle = {"font": {"bold": True},
134-
"border": {"top": "thin",
135-
"right": "thin",
136-
"bottom": "thin",
137-
"left": "thin"},
138-
"align": {"horiz": "center"}}
139-
will be converted to
140-
font: bold on; \
141-
border: top thin, right thin, bottom thin, left thin; \
142-
align: horiz center;
143-
"""
144-
if hasattr(item, "items"):
145-
if firstlevel:
146-
it = [
147-
f"{key}: {cls._style_to_xlwt(value, False)}"
148-
for key, value in item.items()
149-
]
150-
out = f"{(line_sep).join(it)} "
151-
return out
152-
else:
153-
it = [
154-
f"{key} {cls._style_to_xlwt(value, False)}"
155-
for key, value in item.items()
156-
]
157-
out = f"{(field_sep).join(it)} "
158-
return out
159-
else:
160-
item = f"{item}"
161-
item = item.replace("True", "on")
162-
item = item.replace("False", "off")
163-
return item
164-
165-
@classmethod
166-
def _convert_to_style(cls, style_dict, num_format_str=None):
167-
"""
168-
converts a style_dict to an xlwt style object
169-
170-
Parameters
171-
----------
172-
style_dict : style dictionary to convert
173-
num_format_str : optional number format string
174-
"""
175-
import xlwt
176-
177-
if style_dict:
178-
xlwt_stylestr = cls._style_to_xlwt(style_dict)
179-
style = xlwt.easyxf(xlwt_stylestr, field_sep=",", line_sep=";")
180-
else:
181-
style = xlwt.XFStyle()
182-
if num_format_str is not None:
183-
style.num_format_str = num_format_str
184-
185-
return style
111+
def _process_style(self, style):
112+
if style is None:
113+
return None
114+
style_key = json.dumps(style)
115+
if style_key in self.style_dict:
116+
return self.style_dict[style_key]
117+
name = f"pd{len(self.style_dict)+1}"
118+
self.style_dict[style_key] = name
119+
odf_style = Style(name=name, family="table-cell")
120+
if "font" in style:
121+
font = style["font"]
122+
if font.get("bold", False):
123+
odf_style.addElement(TextProperties(fontweight="bold"))
124+
if "borders" in style:
125+
borders = style["borders"]
126+
for side, thickness in borders.items():
127+
thickness_translation = {
128+
"thin": "0.75pt solid #000000"
129+
}
130+
odf_style.addElement(
131+
TableCellProperties(
132+
attributes={f"border{side}": thickness_translation[thickness]}))
133+
if "alignment" in style:
134+
alignment = style["alignment"]
135+
horizontal = alignment.get("horizontal")
136+
if horizontal:
137+
odf_style.addElement(ParagraphProperties(textalign=horizontal))
138+
vertical = alignment.get("vertical")
139+
if vertical:
140+
odf_style.addElement(TableCellProperties(verticalalign=vertical))
141+
self.book.styles.addElement(odf_style)
142+
return name

0 commit comments

Comments
 (0)