1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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
//! Styles define a large number of attributes.
//!
//! They are split along style:family into separate structs like CellStyle,
//! ParagraphStyle etc. These are the main building blocks that will be used
//! to set the properties of each style.
//!
//! Such a style has to be added to the workbook, which returns a reference
//! to the added style (CellStyle -> CellStyleRef). These provide only a loose
//! coupling to the style itself, but allow a better differentiation of the
//! different style-families. Wherever a style can be used a reference
//! of the correct type is expected.
//!
//! ```
//! use spreadsheet_ods::{CellRef, Sheet, WorkBook};
//! use spreadsheet_ods::style::{StyleOrigin, StyleUse, CellStyle};
//! use spreadsheet_ods::color::Rgb;
//! use icu_locid::locale;
//! use spreadsheet_ods::style::stylemap::StyleMap;
//! use spreadsheet_ods::condition::{Condition};
//!
//! let mut wb = WorkBook::new(locale!("en_US"));
//!
//! let mut cs1 = CellStyle::new("ce12", &"num2".into());
//! cs1.set_color(Rgb::new(192, 128, 0));
//! cs1.set_font_bold();
//! let cs1 = wb.add_cellstyle(cs1);
//!
//! let mut cs2 = CellStyle::new("ce11", &"num2".into());
//! cs2.set_color(Rgb::new(0, 192, 128));
//! cs2.set_font_bold();
//! let cs2 = wb.add_cellstyle(cs2);
//!
//! let mut cs3 = CellStyle::new("ce13", &"num4".into());
//! cs3.push_stylemap(StyleMap::new(Condition::content_eq("BB"), "ce12".into(), Some(CellRef::remote("sheet0", 4, 3))));
//! cs3.push_stylemap(StyleMap::new(Condition::content_eq("CC"), "ce11".into(), Some(CellRef::remote("sheet0", 4, 3))));
//! let cs3 = wb.add_cellstyle(cs3);
//!
//!
//!
//! let mut sheet = Sheet::new("sample");
//! sheet.set_styled_value(0, 0, 1234, &cs1);
//! sheet.set_styled_value(0, 1, 5678, &cs2);
//!
//! ```
//!
//! From the specification:
//!
//! The style:style element represents styles.
//!
//! Styles defined by the style:style element use a hierarchical style model. The
//! style:style element supports inheritance of formatting properties by a style from its parent
//! style. A parent style is specified by the style:parent-style-name attribute on a
//! style:style element.
//!
//! The determination of the value of a formatting property begins with any style that is specified by
//! an element. If the formatting property is present in that style, its value is used.
//! If that style does not specify a value for that formatting property and it has a parent style, the value
//! of the formatting element is taken from the parent style, if present.
//! If the parent style does not have a value for the formatting property, the search for the formatting
//! property value continues up parent styles until either the formatting property has been found or a
//! style is found with no parent style.
//! If a search of the parent styles of a style does not result in a value for a formatting property, the
//! determination of its value depends on the style family and the element to which a style is applied.
//! For styles with family text which are applied to elements which are contained in another element
//! that specifies a style with family text, the search continues within the text style that is applied
//! to the nearest ancestor element that specifies a style with family text, and continues in its parent
//! styles.
//!
//! For styles with family text which are applied to elements which are contained in a paragraph
//! element 6.1.1, the search continues within the paragraph style that is applied to the paragraph
//! element, and continues in its parent styles.
//! For styles with family paragraph which are applied to paragraph elements which are contained
//! in a drawing shape or a chart element, the search continues within the graphic, presentation
//! or chart style that is applied to the drawing object or chart element, and continues in its parent
//! styles.
//! For styles with family paragraph which are applied to paragraph elements which are contained
//! in a table cell, the search continues within the table-cell style that is applied to the table-cell,
//! and continues in its parent styles. If a value for the formatting property has not been found, the
//! search continues as defined for styles with family table-cell.
//!
//! For styles with family table-cell which are applied to a table cell, the search continues with the
//! style specified by the table:default-cell-style-name attribute 19.619 of the table cell's
//! table:table-row parent element, if present, and then with the style specified by the
//! table:default-cell-style-name attribute of the table:table-column element
//! associated with the table cell.
//!
//! In all other cases, or if a value for the formatting property has not been found by any of the family
//! specific rules, a default style 16.4 that has the same family as the style initially declared sets the
//! value. If a value has not been found by these steps, but this specification defines a default value,
//! then this default value is used. In all remaining cases an implementation-dependent value is used.

use crate::color::Rgb;
use crate::style::units::{Border, Length, Percent, TextPosition};
use crate::OdsError;
use get_size::GetSize;
use get_size_derive::GetSize;
use std::borrow::Borrow;
use std::str::FromStr;

pub use cellstyle::*;
pub use colstyle::*;
pub use fontface::*;
pub use graphicstyle::*;
pub use masterpage::*;
pub use pagestyle::*;
pub use paragraphstyle::*;
pub use rowstyle::*;
pub use ruby::*;
pub use tablestyle::*;
pub use textstyle::*;

pub mod stylemap;
pub mod tabstop;
pub mod units;

mod cellstyle;
mod colstyle;
mod fontface;
mod graphicstyle;
mod masterpage;
mod pagestyle;
mod paragraphstyle;
mod rowstyle;
mod ruby;
mod tablestyle;
mod textstyle;

// The <style:style> element has the following attributes:
// ok: style:auto-update 19.467,
// ok: style:class 19.470,
// only for table-cell: style:data-style-name 19.473,
// only for paragraph: style:default-outlinelevel 19.474,
// ok: style:display-name 19.476,
// mapped as separate XxxStyle structs: style:family 19.480,
// not used: style:list-level 19.499,
// not used: style:list-style-name 19.500,
// only for table and paragraph: style:master-page-name 19.501,
// ok: style:name 19.502,
// only for paragraph: style:next-style-name 19.503,
// ok: style:parent-style-name 19.510 and
// only for chart: style:percentage-data-style-name 19.511

/// Origin of a style. Content.xml or Styles.xml.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, GetSize)]
pub enum StyleOrigin {
    /// Style comes from Content.xml
    #[default]
    Content,
    /// Style comes from Styles.xml
    Styles,
}

/// Placement of a style. office:styles or office:automatic-styles
/// Defines the usage pattern for the style.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, GetSize)]
pub enum StyleUse {
    /// The style:default-style element represents default styles. A default style specifies
    /// default formatting properties for a style family. These defaults are used if a formatting property is
    /// neither specified by an automatic nor a common style. Default styles exist for all style families that
    /// are represented by the style:style element specified by the style:family attribute
    /// 19.480.
    /// An OpenDocument document should contain the default styles of the style families for which are
    /// used in common or automatic styles in the document.
    Default,
    /// The office:styles element contains common styles used in a document. A common style
    /// is a style chosen by a user for a document or portion thereof.
    Named,
    /// The office:automatic-styles element contains automatic styles used in a document.
    /// An automatic style is a set of formatting properties treated as properties of the object to which the
    /// style is assigned.
    ///
    /// Note: Common and automatic styles behave differently in OpenDocument editing
    /// consumers. Common styles present to a user as a named set of formatting
    /// properties. The formatting properties of an automatic style present to a user as
    /// properties of the object to which the style is applied.
    #[default]
    Automatic,
}

// General style reference.
style_ref2_base!(AnyStyleRef);

/// Parses an attribute string to a value type.
pub(crate) trait ParseStyleAttr<T> {
    fn parse_attr(attr: Option<&str>) -> Result<Option<T>, OdsError>;

    fn parse_attr_def(attr: Option<&str>, default: T) -> Result<T, OdsError> {
        match Self::parse_attr(attr)? {
            None => Ok(default),
            Some(v) => Ok(v),
        }
    }
}

impl ParseStyleAttr<bool> for bool {
    fn parse_attr(attr: Option<&str>) -> Result<Option<bool>, OdsError> {
        if let Some(s) = attr {
            Ok(Some(bool::from_str(s)?))
        } else {
            Ok(None)
        }
    }
}

pub(crate) fn color_string(color: Rgb<u8>) -> String {
    format!("#{:02x}{:02x}{:02x}", color.r, color.g, color.b)
}

pub(crate) fn shadow_string(
    x_offset: Length,
    y_offset: Length,
    blur: Option<Length>,
    color: Rgb<u8>,
) -> String {
    if let Some(blur) = blur {
        format!("{} {} {} {}", color_string(color), x_offset, y_offset, blur)
    } else {
        format!("{} {} {}", color_string(color), x_offset, y_offset)
    }
}

pub(crate) fn rel_width_string(value: f64) -> String {
    format!("{}*", value)
}

pub(crate) fn border_string(width: Length, border: Border, color: Rgb<u8>) -> String {
    format!(
        "{} {} #{:02x}{:02x}{:02x}",
        width, border, color.r, color.g, color.b
    )
}

pub(crate) fn border_line_width_string(inner: Length, space: Length, outer: Length) -> String {
    format!("{} {} {}", inner, space, outer)
}

pub(crate) fn text_position(pos: TextPosition, scale: Option<Percent>) -> String {
    if let Some(scale) = scale {
        format!("{} {}", pos, scale)
    } else {
        format!("{}", pos)
    }
}