Skip to main content

rust_excel_core/
style.rs

1use umya_spreadsheet::structs::PatternValues;
2use umya_spreadsheet::Worksheet;
3
4use crate::types::{BorderEdge, CellStyleDto, MergeRange};
5
6/// Read the style of a cell and return it as a DTO.
7pub fn get_cell_style(sheet: &Worksheet, row: u32, col: u32) -> CellStyleDto {
8    let Some(cell) = sheet.get_cell((col, row)) else {
9        return CellStyleDto::default();
10    };
11
12    let style = cell.get_style();
13
14    let (font_name, font_size, bold, italic, underline, strikethrough, font_color) =
15        if let Some(font) = style.get_font() {
16            (
17                Some(font.get_name().to_string()),
18                Some(*font.get_size()),
19                Some(*font.get_bold()),
20                Some(*font.get_italic()),
21                {
22                    let u = font.get_underline();
23                    Some(!u.is_empty() && u != "none")
24                },
25                Some(*font.get_strikethrough()),
26                Some(font.get_color().get_argb().to_string()),
27            )
28        } else {
29            (None, None, None, None, None, None, None)
30        };
31
32    let bg_color = style
33        .get_fill()
34        .and_then(|fill| fill.get_pattern_fill())
35        .and_then(|pf| pf.get_foreground_color())
36        .map(|c| c.get_argb().to_string());
37
38    let (border_top, border_bottom, border_left, border_right) =
39        if let Some(borders) = style.get_borders() {
40            (
41                Some(BorderEdge {
42                    style: Some(borders.get_top().get_border_style().to_string()),
43                    color: Some(borders.get_top().get_color().get_argb().to_string()),
44                }),
45                Some(BorderEdge {
46                    style: Some(borders.get_bottom().get_border_style().to_string()),
47                    color: Some(borders.get_bottom().get_color().get_argb().to_string()),
48                }),
49                Some(BorderEdge {
50                    style: Some(borders.get_left().get_border_style().to_string()),
51                    color: Some(borders.get_left().get_color().get_argb().to_string()),
52                }),
53                Some(BorderEdge {
54                    style: Some(borders.get_right().get_border_style().to_string()),
55                    color: Some(borders.get_right().get_color().get_argb().to_string()),
56                }),
57            )
58        } else {
59            (None, None, None, None)
60        };
61
62    let (horizontal_align, vertical_align, wrap_text) =
63        if let Some(alignment) = style.get_alignment() {
64            (
65                Some(format!("{:?}", alignment.get_horizontal())),
66                Some(format!("{:?}", alignment.get_vertical())),
67                Some(*alignment.get_wrap_text()),
68            )
69        } else {
70            (None, None, None)
71        };
72
73    let number_format = style
74        .get_number_format()
75        .map(|nf| nf.get_format_code().to_string());
76
77    CellStyleDto {
78        font_name,
79        font_size,
80        bold,
81        italic,
82        underline,
83        strikethrough,
84        font_color,
85        bg_color,
86        border_top,
87        border_bottom,
88        border_left,
89        border_right,
90        horizontal_align,
91        vertical_align,
92        wrap_text,
93        number_format,
94    }
95}
96
97/// Apply a partial style DTO to a single cell. Only non-None fields are applied.
98pub fn apply_cell_style(sheet: &mut Worksheet, row: u32, col: u32, dto: &CellStyleDto) {
99    let cell = sheet.get_cell_mut((col, row));
100    let style = cell.get_style_mut();
101
102    // Font
103    if let Some(ref name) = dto.font_name {
104        style.get_font_mut().set_name(name);
105    }
106    if let Some(size) = dto.font_size {
107        style.get_font_mut().set_size(size);
108    }
109    if let Some(bold) = dto.bold {
110        style.get_font_mut().set_bold(bold);
111    }
112    if let Some(italic) = dto.italic {
113        style.get_font_mut().set_italic(italic);
114    }
115    if let Some(underline) = dto.underline {
116        let val = if underline { "single" } else { "none" };
117        style.get_font_mut().set_underline(val);
118    }
119    if let Some(strike) = dto.strikethrough {
120        style.get_font_mut().set_strikethrough(strike);
121    }
122    if let Some(ref color) = dto.font_color {
123        style.get_font_mut().get_color_mut().set_argb(color);
124    }
125
126    // Fill
127    if let Some(ref bg) = dto.bg_color {
128        style
129            .get_fill_mut()
130            .get_pattern_fill_mut()
131            .get_foreground_color_mut()
132            .set_argb(bg);
133        style
134            .get_fill_mut()
135            .get_pattern_fill_mut()
136            .set_pattern_type(PatternValues::Solid);
137    }
138
139    // Borders
140    if let Some(ref edge) = dto.border_top {
141        let border = style.get_borders_mut().get_top_mut();
142        if let Some(ref s) = edge.style {
143            border.set_border_style(s);
144        }
145        if let Some(ref c) = edge.color {
146            border.get_color_mut().set_argb(c);
147        }
148    }
149    if let Some(ref edge) = dto.border_bottom {
150        let border = style.get_borders_mut().get_bottom_mut();
151        if let Some(ref s) = edge.style {
152            border.set_border_style(s);
153        }
154        if let Some(ref c) = edge.color {
155            border.get_color_mut().set_argb(c);
156        }
157    }
158    if let Some(ref edge) = dto.border_left {
159        let border = style.get_borders_mut().get_left_mut();
160        if let Some(ref s) = edge.style {
161            border.set_border_style(s);
162        }
163        if let Some(ref c) = edge.color {
164            border.get_color_mut().set_argb(c);
165        }
166    }
167    if let Some(ref edge) = dto.border_right {
168        let border = style.get_borders_mut().get_right_mut();
169        if let Some(ref s) = edge.style {
170            border.set_border_style(s);
171        }
172        if let Some(ref c) = edge.color {
173            border.get_color_mut().set_argb(c);
174        }
175    }
176
177    // Alignment — parse string back to enum
178    if let Some(ref h) = dto.horizontal_align {
179        if let Some(val) = parse_horizontal_align(h) {
180            style.get_alignment_mut().set_horizontal(val);
181        }
182    }
183    if let Some(ref v) = dto.vertical_align {
184        if let Some(val) = parse_vertical_align(v) {
185            style.get_alignment_mut().set_vertical(val);
186        }
187    }
188    if let Some(wrap) = dto.wrap_text {
189        style.get_alignment_mut().set_wrap_text(wrap);
190    }
191
192    // Number format
193    if let Some(ref fmt) = dto.number_format {
194        style.get_number_format_mut().set_format_code(fmt);
195    }
196}
197
198/// Apply a partial style DTO to a range of cells.
199pub fn apply_range_style(sheet: &mut Worksheet, range: &MergeRange, dto: &CellStyleDto) {
200    for r in range.start_row..=range.end_row {
201        for c in range.start_col..=range.end_col {
202            apply_cell_style(sheet, r, c, dto);
203        }
204    }
205}
206
207fn parse_horizontal_align(s: &str) -> Option<umya_spreadsheet::structs::HorizontalAlignmentValues> {
208    use umya_spreadsheet::structs::HorizontalAlignmentValues::*;
209    match s.to_lowercase().as_str() {
210        "center" => Some(Center),
211        "centercontinuous" => Some(CenterContinuous),
212        "distributed" => Some(Distributed),
213        "fill" => Some(Fill),
214        "general" => Some(General),
215        "justify" => Some(Justify),
216        "left" => Some(Left),
217        "right" => Some(Right),
218        _ => None,
219    }
220}
221
222fn parse_vertical_align(s: &str) -> Option<umya_spreadsheet::structs::VerticalAlignmentValues> {
223    use umya_spreadsheet::structs::VerticalAlignmentValues::*;
224    match s.to_lowercase().as_str() {
225        "bottom" => Some(Bottom),
226        "center" => Some(Center),
227        "distributed" => Some(Distributed),
228        "justify" => Some(Justify),
229        "top" => Some(Top),
230        _ => None,
231    }
232}