spreadsheet_ods/style/
mod.rs

1//! Styles define a large number of attributes.
2//!
3//! They are split along style:family into separate structs like CellStyle,
4//! ParagraphStyle etc. These are the main building blocks that will be used
5//! to set the properties of each style.
6//!
7//! Such a style has to be added to the workbook, which returns a reference
8//! to the added style (CellStyle -> CellStyleRef). These provide only a loose
9//! coupling to the style itself, but allow a better differentiation of the
10//! different style-families. Wherever a style can be used a reference
11//! of the correct type is expected.
12//!
13//! ```
14//! use spreadsheet_ods::{CellRef, Sheet, WorkBook};
15//! use spreadsheet_ods::style::{StyleOrigin, StyleUse, CellStyle};
16//! use spreadsheet_ods::color::Rgb;
17//! use icu_locale_core::locale;
18//! use spreadsheet_ods::style::stylemap::StyleMap;
19//! use spreadsheet_ods::condition::{Condition};
20//!
21//! let mut wb = WorkBook::new(locale!("en-US"));
22//!
23//! let mut cs1 = CellStyle::new("ce12", &"num2".into());
24//! cs1.set_color(Rgb::new(192, 128, 0));
25//! cs1.set_font_bold();
26//! let cs1 = wb.add_cellstyle(cs1);
27//!
28//! let mut cs2 = CellStyle::new("ce11", &"num2".into());
29//! cs2.set_color(Rgb::new(0, 192, 128));
30//! cs2.set_font_bold();
31//! let cs2 = wb.add_cellstyle(cs2);
32//!
33//! let mut cs3 = CellStyle::new("ce13", &"num4".into());
34//! cs3.push_stylemap(StyleMap::new(Condition::content_eq("BB"), "ce12".into(), Some(CellRef::remote("sheet0", 4, 3))));
35//! cs3.push_stylemap(StyleMap::new(Condition::content_eq("CC"), "ce11".into(), Some(CellRef::remote("sheet0", 4, 3))));
36//! let cs3 = wb.add_cellstyle(cs3);
37//!
38//!
39//!
40//! let mut sheet = Sheet::new("sample");
41//! sheet.set_styled_value(0, 0, 1234, &cs1);
42//! sheet.set_styled_value(0, 1, 5678, &cs2);
43//!
44//! ```
45//!
46//! From the specification:
47//!
48//! The style:style element represents styles.
49//!
50//! Styles defined by the style:style element use a hierarchical style model. The
51//! style:style element supports inheritance of formatting properties by a style from its parent
52//! style. A parent style is specified by the style:parent-style-name attribute on a
53//! style:style element.
54//!
55//! The determination of the value of a formatting property begins with any style that is specified by
56//! an element. If the formatting property is present in that style, its value is used.
57//! If that style does not specify a value for that formatting property and it has a parent style, the value
58//! of the formatting element is taken from the parent style, if present.
59//! If the parent style does not have a value for the formatting property, the search for the formatting
60//! property value continues up parent styles until either the formatting property has been found or a
61//! style is found with no parent style.
62//! If a search of the parent styles of a style does not result in a value for a formatting property, the
63//! determination of its value depends on the style family and the element to which a style is applied.
64//! For styles with family text which are applied to elements which are contained in another element
65//! that specifies a style with family text, the search continues within the text style that is applied
66//! to the nearest ancestor element that specifies a style with family text, and continues in its parent
67//! styles.
68//!
69//! For styles with family text which are applied to elements which are contained in a paragraph
70//! element 6.1.1, the search continues within the paragraph style that is applied to the paragraph
71//! element, and continues in its parent styles.
72//! For styles with family paragraph which are applied to paragraph elements which are contained
73//! in a drawing shape or a chart element, the search continues within the graphic, presentation
74//! or chart style that is applied to the drawing object or chart element, and continues in its parent
75//! styles.
76//! For styles with family paragraph which are applied to paragraph elements which are contained
77//! in a table cell, the search continues within the table-cell style that is applied to the table-cell,
78//! and continues in its parent styles. If a value for the formatting property has not been found, the
79//! search continues as defined for styles with family table-cell.
80//!
81//! For styles with family table-cell which are applied to a table cell, the search continues with the
82//! style specified by the table:default-cell-style-name attribute 19.619 of the table cell's
83//! table:table-row parent element, if present, and then with the style specified by the
84//! table:default-cell-style-name attribute of the table:table-column element
85//! associated with the table cell.
86//!
87//! In all other cases, or if a value for the formatting property has not been found by any of the family
88//! specific rules, a default style 16.4 that has the same family as the style initially declared sets the
89//! value. If a value has not been found by these steps, but this specification defines a default value,
90//! then this default value is used. In all remaining cases an implementation-dependent value is used.
91
92use crate::color::Rgb;
93use crate::style::units::{Border, Length, Percent, TextPosition};
94use crate::OdsError;
95use get_size2::GetSize;
96use std::borrow::Borrow;
97use std::str::FromStr;
98
99pub use cellstyle::*;
100pub use colstyle::*;
101pub use fontface::*;
102pub use graphicstyle::*;
103pub use masterpage::*;
104pub use pagestyle::*;
105pub use paragraphstyle::*;
106pub use rowstyle::*;
107pub use ruby::*;
108pub use tablestyle::*;
109pub use textstyle::*;
110
111pub mod stylemap;
112pub mod tabstop;
113pub mod units;
114
115mod cellstyle;
116mod colstyle;
117mod fontface;
118mod graphicstyle;
119mod masterpage;
120mod pagestyle;
121mod paragraphstyle;
122mod rowstyle;
123mod ruby;
124mod tablestyle;
125mod textstyle;
126
127// The <style:style> element has the following attributes:
128// ok: style:auto-update 19.467,
129// ok: style:class 19.470,
130// only for table-cell: style:data-style-name 19.473,
131// only for paragraph: style:default-outlinelevel 19.474,
132// ok: style:display-name 19.476,
133// mapped as separate XxxStyle structs: style:family 19.480,
134// not used: style:list-level 19.499,
135// not used: style:list-style-name 19.500,
136// only for table and paragraph: style:master-page-name 19.501,
137// ok: style:name 19.502,
138// only for paragraph: style:next-style-name 19.503,
139// ok: style:parent-style-name 19.510 and
140// only for chart: style:percentage-data-style-name 19.511
141
142/// Origin of a style. Content.xml or Styles.xml.
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, GetSize)]
144pub enum StyleOrigin {
145    /// Style comes from Content.xml
146    #[default]
147    Content,
148    /// Style comes from Styles.xml
149    Styles,
150}
151
152/// Placement of a style. office:styles or office:automatic-styles
153/// Defines the usage pattern for the style.
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, GetSize)]
155pub enum StyleUse {
156    /// The style:default-style element represents default styles. A default style specifies
157    /// default formatting properties for a style family. These defaults are used if a formatting property is
158    /// neither specified by an automatic nor a common style. Default styles exist for all style families that
159    /// are represented by the style:style element specified by the style:family attribute
160    /// 19.480.
161    /// An OpenDocument document should contain the default styles of the style families for which are
162    /// used in common or automatic styles in the document.
163    Default,
164    /// The office:styles element contains common styles used in a document. A common style
165    /// is a style chosen by a user for a document or portion thereof.
166    Named,
167    /// The office:automatic-styles element contains automatic styles used in a document.
168    /// An automatic style is a set of formatting properties treated as properties of the object to which the
169    /// style is assigned.
170    ///
171    /// Note: Common and automatic styles behave differently in OpenDocument editing
172    /// consumers. Common styles present to a user as a named set of formatting
173    /// properties. The formatting properties of an automatic style present to a user as
174    /// properties of the object to which the style is applied.
175    #[default]
176    Automatic,
177}
178
179// General style reference.
180style_ref2_base!(AnyStyleRef);
181
182/// Parses an attribute string to a value type.
183pub(crate) trait ParseStyleAttr<T> {
184    fn parse_attr(attr: Option<&str>) -> Result<Option<T>, OdsError>;
185
186    fn parse_attr_def(attr: Option<&str>, default: T) -> Result<T, OdsError> {
187        match Self::parse_attr(attr)? {
188            None => Ok(default),
189            Some(v) => Ok(v),
190        }
191    }
192}
193
194impl ParseStyleAttr<bool> for bool {
195    fn parse_attr(attr: Option<&str>) -> Result<Option<bool>, OdsError> {
196        if let Some(s) = attr {
197            Ok(Some(bool::from_str(s)?))
198        } else {
199            Ok(None)
200        }
201    }
202}
203
204pub(crate) fn color_string(color: Rgb<u8>) -> String {
205    format!("#{:02x}{:02x}{:02x}", color.r, color.g, color.b)
206}
207
208pub(crate) fn shadow_string(
209    x_offset: Length,
210    y_offset: Length,
211    blur: Option<Length>,
212    color: Rgb<u8>,
213) -> String {
214    if let Some(blur) = blur {
215        format!("{} {} {} {}", color_string(color), x_offset, y_offset, blur)
216    } else {
217        format!("{} {} {}", color_string(color), x_offset, y_offset)
218    }
219}
220
221pub(crate) fn rel_width_string(value: f64) -> String {
222    format!("{}*", value)
223}
224
225pub(crate) fn border_string(width: Length, border: Border, color: Rgb<u8>) -> String {
226    format!(
227        "{} {} #{:02x}{:02x}{:02x}",
228        width, border, color.r, color.g, color.b
229    )
230}
231
232pub(crate) fn border_line_width_string(inner: Length, space: Length, outer: Length) -> String {
233    format!("{} {} {}", inner, space, outer)
234}
235
236pub(crate) fn text_position(pos: TextPosition, scale: Option<Percent>) -> String {
237    if let Some(scale) = scale {
238        format!("{} {}", pos, scale)
239    } else {
240        format!("{}", pos)
241    }
242}