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_locid::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_size::GetSize;
96use get_size_derive::GetSize;
97use std::borrow::Borrow;
98use std::str::FromStr;
99
100pub use cellstyle::*;
101pub use colstyle::*;
102pub use fontface::*;
103pub use graphicstyle::*;
104pub use masterpage::*;
105pub use pagestyle::*;
106pub use paragraphstyle::*;
107pub use rowstyle::*;
108pub use ruby::*;
109pub use tablestyle::*;
110pub use textstyle::*;
111
112pub mod stylemap;
113pub mod tabstop;
114pub mod units;
115
116mod cellstyle;
117mod colstyle;
118mod fontface;
119mod graphicstyle;
120mod masterpage;
121mod pagestyle;
122mod paragraphstyle;
123mod rowstyle;
124mod ruby;
125mod tablestyle;
126mod textstyle;
127
128// The <style:style> element has the following attributes:
129// ok: style:auto-update 19.467,
130// ok: style:class 19.470,
131// only for table-cell: style:data-style-name 19.473,
132// only for paragraph: style:default-outlinelevel 19.474,
133// ok: style:display-name 19.476,
134// mapped as separate XxxStyle structs: style:family 19.480,
135// not used: style:list-level 19.499,
136// not used: style:list-style-name 19.500,
137// only for table and paragraph: style:master-page-name 19.501,
138// ok: style:name 19.502,
139// only for paragraph: style:next-style-name 19.503,
140// ok: style:parent-style-name 19.510 and
141// only for chart: style:percentage-data-style-name 19.511
142
143/// Origin of a style. Content.xml or Styles.xml.
144#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, GetSize)]
145pub enum StyleOrigin {
146    /// Style comes from Content.xml
147    #[default]
148    Content,
149    /// Style comes from Styles.xml
150    Styles,
151}
152
153/// Placement of a style. office:styles or office:automatic-styles
154/// Defines the usage pattern for the style.
155#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, GetSize)]
156pub enum StyleUse {
157    /// The style:default-style element represents default styles. A default style specifies
158    /// default formatting properties for a style family. These defaults are used if a formatting property is
159    /// neither specified by an automatic nor a common style. Default styles exist for all style families that
160    /// are represented by the style:style element specified by the style:family attribute
161    /// 19.480.
162    /// An OpenDocument document should contain the default styles of the style families for which are
163    /// used in common or automatic styles in the document.
164    Default,
165    /// The office:styles element contains common styles used in a document. A common style
166    /// is a style chosen by a user for a document or portion thereof.
167    Named,
168    /// The office:automatic-styles element contains automatic styles used in a document.
169    /// An automatic style is a set of formatting properties treated as properties of the object to which the
170    /// style is assigned.
171    ///
172    /// Note: Common and automatic styles behave differently in OpenDocument editing
173    /// consumers. Common styles present to a user as a named set of formatting
174    /// properties. The formatting properties of an automatic style present to a user as
175    /// properties of the object to which the style is applied.
176    #[default]
177    Automatic,
178}
179
180// General style reference.
181style_ref2_base!(AnyStyleRef);
182
183/// Parses an attribute string to a value type.
184pub(crate) trait ParseStyleAttr<T> {
185    fn parse_attr(attr: Option<&str>) -> Result<Option<T>, OdsError>;
186
187    fn parse_attr_def(attr: Option<&str>, default: T) -> Result<T, OdsError> {
188        match Self::parse_attr(attr)? {
189            None => Ok(default),
190            Some(v) => Ok(v),
191        }
192    }
193}
194
195impl ParseStyleAttr<bool> for bool {
196    fn parse_attr(attr: Option<&str>) -> Result<Option<bool>, OdsError> {
197        if let Some(s) = attr {
198            Ok(Some(bool::from_str(s)?))
199        } else {
200            Ok(None)
201        }
202    }
203}
204
205pub(crate) fn color_string(color: Rgb<u8>) -> String {
206    format!("#{:02x}{:02x}{:02x}", color.r, color.g, color.b)
207}
208
209pub(crate) fn shadow_string(
210    x_offset: Length,
211    y_offset: Length,
212    blur: Option<Length>,
213    color: Rgb<u8>,
214) -> String {
215    if let Some(blur) = blur {
216        format!("{} {} {} {}", color_string(color), x_offset, y_offset, blur)
217    } else {
218        format!("{} {} {}", color_string(color), x_offset, y_offset)
219    }
220}
221
222pub(crate) fn rel_width_string(value: f64) -> String {
223    format!("{}*", value)
224}
225
226pub(crate) fn border_string(width: Length, border: Border, color: Rgb<u8>) -> String {
227    format!(
228        "{} {} #{:02x}{:02x}{:02x}",
229        width, border, color.r, color.g, color.b
230    )
231}
232
233pub(crate) fn border_line_width_string(inner: Length, space: Length, outer: Length) -> String {
234    format!("{} {} {}", inner, space, outer)
235}
236
237pub(crate) fn text_position(pos: TextPosition, scale: Option<Percent>) -> String {
238    if let Some(scale) = scale {
239        format!("{} {}", pos, scale)
240    } else {
241        format!("{}", pos)
242    }
243}