///|
/// Number format specification for cell values.
///
/// Excel supports both built-in formats (by index) and custom format strings.
///
/// # Built-in Format IDs
/// - 0: General
/// - 1: 0
/// - 2: 0.00
/// - 3: #,##0
/// - 4: #,##0.00
/// - 9: 0%
/// - 10: 0.00%
/// - 11: 0.00E+00
/// - 12: # ?/?
/// - 13: # ??/??
/// - 14: mm-dd-yy
/// - 15: d-mmm-yy
/// - 16: d-mmm
/// - 17: mmm-yy
/// - 18: h:mm AM/PM
/// - 19: h:mm:ss AM/PM
/// - 20: h:mm
/// - 21: h:mm:ss
/// - 22: m/d/yy h:mm
pub enum NumberFormat {
Builtin(Int)
Custom(String)
} derive(Eq, Debug)
///|
/// Font styling for cell text.
///
/// All fields are optional; only set fields affect the cell's appearance.
///
/// # Example
/// ```mbt nocheck
/// let font = Font::with_values(
/// bold=true,
/// size=12.0,
/// color="FF0000", // Red
/// underline="single",
/// )
/// ```
pub struct Font {
mut bold : Bool?
mut italic : Bool?
mut strike : Bool?
mut outline : Bool?
mut shadow : Bool?
mut condense : Bool?
mut extended : Bool?
mut underline : String?
mut size : Double?
mut color : String?
mut color_theme : Int?
mut color_indexed : Int?
mut color_tint : Double?
mut charset : Int?
mut family_number : Int?
mut scheme : String?
mut vert_align : String?
mut family : String?
} derive(Eq, Debug)
///|
/// Creates a new Font with all fields set to None (default styling).
fn Font::new() -> Font {
{
bold: None,
italic: None,
strike: None,
outline: None,
shadow: None,
condense: None,
extended: None,
underline: None,
size: None,
color: None,
color_theme: None,
color_indexed: None,
color_tint: None,
charset: None,
family_number: None,
scheme: None,
vert_align: None,
family: None,
}
}
///|
/// Creates a new Font with specified values.
///
/// # Parameters
/// - `bold`: Bold text
/// - `italic`: Italic text
/// - `strike`: Strikethrough
/// - `underline`: Underline style ("single", "double", "singleAccounting", "doubleAccounting")
/// - `size`: Font size in points
/// - `color`: Hex color code like "FF0000" for red
/// - `color_theme`: Theme color index (0-9)
/// - `color_tint`: Tint value for theme color (-1.0 to 1.0)
/// - `vert_align`: Vertical alignment ("superscript", "subscript")
/// - `family`: Font family name
pub fn Font::with_values(
bold? : Bool,
italic? : Bool,
strike? : Bool,
outline? : Bool,
shadow? : Bool,
condense? : Bool,
extended? : Bool,
underline? : String,
size? : Double,
color? : String,
color_theme? : Int,
color_indexed? : Int,
color_tint? : Double,
charset? : Int,
family_number? : Int,
scheme? : String,
vert_align? : String,
family? : String,
) -> Font {
let font = Font::new()
match bold {
Some(v) => font.bold = Some(v)
None => ()
}
match italic {
Some(v) => font.italic = Some(v)
None => ()
}
match strike {
Some(v) => font.strike = Some(v)
None => ()
}
match outline {
Some(v) => font.outline = Some(v)
None => ()
}
match shadow {
Some(v) => font.shadow = Some(v)
None => ()
}
match condense {
Some(v) => font.condense = Some(v)
None => ()
}
match extended {
Some(v) => font.extended = Some(v)
None => ()
}
match underline {
Some(v) => font.underline = Some(v)
None => ()
}
match size {
Some(v) => font.size = Some(v)
None => ()
}
match color {
Some(v) => font.color = Some(v)
None => ()
}
match color_theme {
Some(v) => font.color_theme = Some(v)
None => ()
}
match color_indexed {
Some(v) => font.color_indexed = Some(v)
None => ()
}
match color_tint {
Some(v) => font.color_tint = Some(v)
None => ()
}
match charset {
Some(v) => font.charset = Some(v)
None => ()
}
match family_number {
Some(v) => font.family_number = Some(v)
None => ()
}
match scheme {
Some(v) => font.scheme = Some(v)
None => ()
}
match vert_align {
Some(v) => font.vert_align = Some(v)
None => ()
}
match family {
Some(v) => font.family = Some(v)
None => ()
}
font
}
///|
/// Fill (background) styling for cells.
///
/// Supports solid colors, patterns, and gradients.
///
/// # Example
/// ```mbt nocheck
/// // Solid yellow fill
/// let fill = Fill::solid("FFFF00")
///
/// // Gradient fill
///
/// let gradient = Fill::gradient("FF0000", "0000FF", shading=1)
/// ```
pub struct Fill {
mut typ : String?
mut pattern : Int?
mut shading : Int?
mut colors : Array[String]?
mut transparency : Int?
mut fg_theme : Int?
mut fg_indexed : Int?
mut fg_tint : Double?
mut bg_theme : Int?
mut bg_indexed : Int?
mut bg_tint : Double?
} derive(Eq, Debug)
///|
fn Fill::new() -> Fill {
{
typ: None,
pattern: None,
shading: None,
colors: None,
transparency: None,
fg_theme: None,
fg_indexed: None,
fg_tint: None,
bg_theme: None,
bg_indexed: None,
bg_tint: None,
}
}
///|
pub fn Fill::pattern(
pattern? : Int,
color? : String,
transparency? : Int,
) -> Fill {
let fill = Fill::new()
fill.typ = Some("pattern")
match pattern {
Some(v) => fill.pattern = Some(v)
None => ()
}
match color {
Some(v) => fill.colors = Some([v])
None => ()
}
match transparency {
Some(v) => fill.transparency = Some(v)
None => ()
}
fill
}
///|
/// Creates a solid color fill.
///
/// # Parameters
/// - `color`: Hex color code like "FFFF00" for yellow
/// - `transparency`: Transparency percentage (0-100)
///
/// # Example
/// ```mbt nocheck
/// let yellow = Fill::solid("FFFF00")
///
/// let semi_transparent = Fill::solid("FF0000", transparency=50)
/// ```
pub fn Fill::solid(color : String, transparency? : Int) -> Fill {
Fill::pattern(pattern=1, color~, transparency?)
}
///|
priv struct GradientVariant {
bottom : Double
degree : Double
left : Double
right : Double
top : Double
typ : String
stops : Array[Double]
}
///|
let fill_gradient_variants : Array[GradientVariant] = [
{
bottom: 0.0,
degree: 90.0,
left: 0.0,
right: 0.0,
top: 0.0,
typ: "",
stops: [0.0, 1.0],
},
{
bottom: 0.0,
degree: 270.0,
left: 0.0,
right: 0.0,
top: 0.0,
typ: "",
stops: [0.0, 1.0],
},
{
bottom: 0.0,
degree: 90.0,
left: 0.0,
right: 0.0,
top: 0.0,
typ: "",
stops: [0.0, 0.5, 1.0],
},
{
bottom: 0.0,
degree: 0.0,
left: 0.0,
right: 0.0,
top: 0.0,
typ: "",
stops: [0.0, 1.0],
},
{
bottom: 0.0,
degree: 180.0,
left: 0.0,
right: 0.0,
top: 0.0,
typ: "",
stops: [0.0, 1.0],
},
{
bottom: 0.0,
degree: 0.0,
left: 0.0,
right: 0.0,
top: 0.0,
typ: "",
stops: [0.0, 0.5, 1.0],
},
{
bottom: 0.0,
degree: 45.0,
left: 0.0,
right: 0.0,
top: 0.0,
typ: "",
stops: [0.0, 1.0],
},
{
bottom: 0.0,
degree: 255.0,
left: 0.0,
right: 0.0,
top: 0.0,
typ: "",
stops: [0.0, 1.0],
},
{
bottom: 0.0,
degree: 45.0,
left: 0.0,
right: 0.0,
top: 0.0,
typ: "",
stops: [0.0, 0.5, 1.0],
},
{
bottom: 0.0,
degree: 135.0,
left: 0.0,
right: 0.0,
top: 0.0,
typ: "",
stops: [0.0, 1.0],
},
{
bottom: 0.0,
degree: 315.0,
left: 0.0,
right: 0.0,
top: 0.0,
typ: "",
stops: [0.0, 1.0],
},
{
bottom: 0.0,
degree: 135.0,
left: 0.0,
right: 0.0,
top: 0.0,
typ: "",
stops: [0.0, 0.5, 1.0],
},
{
bottom: 0.0,
degree: 0.0,
left: 0.0,
right: 0.0,
top: 0.0,
typ: "path",
stops: [0.0, 1.0],
},
{
bottom: 0.0,
degree: 0.0,
left: 1.0,
right: 1.0,
top: 0.0,
typ: "path",
stops: [0.0, 1.0],
},
{
bottom: 1.0,
degree: 0.0,
left: 0.0,
right: 0.0,
top: 1.0,
typ: "path",
stops: [0.0, 1.0],
},
{
bottom: 1.0,
degree: 0.0,
left: 1.0,
right: 1.0,
top: 1.0,
typ: "path",
stops: [0.0, 1.0],
},
{
bottom: 0.5,
degree: 0.0,
left: 0.5,
right: 0.5,
top: 0.5,
typ: "path",
stops: [0.0, 1.0],
},
]
///|
/// Creates a gradient fill between two colors.
///
/// # Parameters
/// - `color1`: Start color (hex code)
/// - `color2`: End color (hex code)
/// - `shading`: Gradient shading variant (0-15)
/// - `transparency`: Transparency percentage (0-100)
///
/// # Example
/// ```mbt nocheck
/// let gradient = Fill::gradient("FF0000", "0000FF") // Red to blue
/// ```
pub fn Fill::gradient(
color1 : String,
color2 : String,
shading? : Int = 0,
transparency? : Int,
) -> Fill {
let fill = Fill::new()
fill.typ = Some("gradient")
fill.shading = Some(shading)
fill.colors = Some([color1, color2])
match transparency {
Some(v) => fill.transparency = Some(v)
None => ()
}
fill
}
///|
pub fn Fill::solid_theme(theme : Int, tint? : Double) -> Fill {
let fill = Fill::new()
fill.typ = Some("pattern")
fill.pattern = Some(1)
fill.fg_theme = Some(theme)
match tint {
Some(v) => fill.fg_tint = Some(v)
None => ()
}
fill
}
///|
pub fn Fill::solid_indexed(indexed : Int, tint? : Double) -> Fill {
let fill = Fill::new()
fill.typ = Some("pattern")
fill.pattern = Some(1)
fill.fg_indexed = Some(indexed)
match tint {
Some(v) => fill.fg_tint = Some(v)
None => ()
}
fill
}
///|
pub fn Fill::solid_bg_theme(theme : Int, tint? : Double) -> Fill {
let fill = Fill::new()
fill.typ = Some("pattern")
fill.pattern = Some(1)
fill.bg_theme = Some(theme)
match tint {
Some(v) => fill.bg_tint = Some(v)
None => ()
}
fill
}
///|
pub fn Fill::solid_bg_indexed(indexed : Int, tint? : Double) -> Fill {
let fill = Fill::new()
fill.typ = Some("pattern")
fill.pattern = Some(1)
fill.bg_indexed = Some(indexed)
match tint {
Some(v) => fill.bg_tint = Some(v)
None => ()
}
fill
}
///|
/// Border styling for a cell edge.
///
/// # Border Types
/// - "left", "right", "top", "bottom": Cell edges
/// - "diagonalUp", "diagonalDown": Diagonal lines
///
/// # Border Styles
/// - 0: None
/// - 1: Thin
/// - 2: Medium
/// - 3: Dashed
/// - 4: Dotted
/// - 5: Thick
/// - 6: Double
/// - 7: Hair
/// - 8: MediumDashed
/// - 9: DashDot
/// - 10: MediumDashDot
/// - 11: DashDotDot
/// - 12: MediumDashDotDot
/// - 13: SlantDashDot
pub struct Border {
typ : String
mut color : String?
mut style : Int?
} derive(Eq, Debug)
///|
priv struct BorderXmlEntry {
diagonal_up : Bool
diagonal_down : Bool
lines : Array[(String, Int, String?)]
}
///|
fn Border::new(typ : String) -> Border {
{ typ, color: None, style: None }
}
///|
/// Creates a Border with specified values.
///
/// # Parameters
/// - `typ`: Border type ("left", "right", "top", "bottom")
/// - `color`: Hex color code
/// - `style`: Border style (1=thin, 2=medium, 5=thick, etc.)
///
/// # Example
/// ```mbt nocheck
/// let thin_black = Border::with_values("left", color="000000", style=1)
///
/// let thick_red = Border::with_values("bottom", color="FF0000", style=5)
/// ```
pub fn Border::with_values(
typ : String,
color? : String,
style? : Int,
) -> Border {
let border = Border::new(typ)
match color {
Some(v) => border.color = Some(v)
None => ()
}
match style {
Some(v) => border.style = Some(v)
None => ()
}
border
}
///|
pub struct Protection {
mut hidden : Bool?
mut locked : Bool?
} derive(Eq, Debug)
///|
fn Protection::new() -> Protection {
{ hidden: None, locked: None }
}
///|
pub fn Protection::with_values(hidden? : Bool, locked? : Bool) -> Protection {
let protection = Protection::new()
match hidden {
Some(v) => protection.hidden = Some(v)
None => ()
}
match locked {
Some(v) => protection.locked = Some(v)
None => ()
}
protection
}
///|
/// Text alignment settings for cells.
///
/// # Horizontal Values
/// - "left", "center", "right", "fill", "justify", "centerContinuous", "distributed"
///
/// # Vertical Values
/// - "top", "center", "bottom", "justify", "distributed"
pub struct Alignment {
mut horizontal : String?
mut vertical : String?
mut wrap_text : Bool?
mut text_rotation : Int?
mut indent : Int?
mut shrink_to_fit : Bool?
mut justify_last_line : Bool?
mut reading_order : Int?
mut relative_indent : Int?
} derive(Eq, Debug)
///|
fn Alignment::new() -> Alignment {
{
horizontal: None,
vertical: None,
wrap_text: None,
text_rotation: None,
indent: None,
shrink_to_fit: None,
justify_last_line: None,
reading_order: None,
relative_indent: None,
}
}
///|
/// Creates an Alignment with specified values.
///
/// # Parameters
/// - `horizontal`: Horizontal alignment ("left", "center", "right", etc.)
/// - `vertical`: Vertical alignment ("top", "center", "bottom", etc.)
/// - `wrap_text`: Enable text wrapping
/// - `text_rotation`: Rotation angle in degrees (0-180, or 255 for vertical)
/// - `indent`: Indentation level
/// - `shrink_to_fit`: Shrink text to fit cell width
pub fn Alignment::with_values(
horizontal? : String,
vertical? : String,
wrap_text? : Bool,
text_rotation? : Int,
indent? : Int,
shrink_to_fit? : Bool,
justify_last_line? : Bool,
reading_order? : Int,
relative_indent? : Int,
) -> Alignment {
let align = Alignment::new()
match horizontal {
Some(v) => align.horizontal = Some(v)
None => ()
}
match vertical {
Some(v) => align.vertical = Some(v)
None => ()
}
match wrap_text {
Some(v) => align.wrap_text = Some(v)
None => ()
}
match text_rotation {
Some(v) => align.text_rotation = Some(v)
None => ()
}
match indent {
Some(v) => align.indent = Some(v)
None => ()
}
match shrink_to_fit {
Some(v) => align.shrink_to_fit = Some(v)
None => ()
}
match justify_last_line {
Some(v) => align.justify_last_line = Some(v)
None => ()
}
match reading_order {
Some(v) => align.reading_order = Some(v)
None => ()
}
match relative_indent {
Some(v) => align.relative_indent = Some(v)
None => ()
}
align
}
///|
/// Complete cell style specification.
///
/// A Style combines font, fill, border, alignment, number format, and protection
/// settings. Styles are registered with a workbook and referenced by ID in cells.
///
/// # Example
/// ```mbt nocheck
/// // Create a style with multiple elements
/// let style = Style::new()
/// .with_font(Font::with_values(bold=true, size=12.0))
/// .with_fill(Fill::solid("FFFF00"))
/// .with_alignment(Alignment::with_values(horizontal="center"))
///
/// // Register with workbook and apply to cell
/// let style_id = workbook.add_style(style)
/// sheet.set_cell_style("A1", style_id)
/// ```
pub struct Style {
number_format : NumberFormat?
num_fmt : Int?
decimal_places : Int?
custom_num_fmt : String?
neg_red : Bool?
font : Font?
fill : Fill?
border : Array[Border]?
protection : Protection?
alignment : Alignment?
} derive(Eq, Debug)
///|
/// Creates a new empty Style with all fields set to None.
pub fn Style::new() -> Style {
{
number_format: None,
num_fmt: None,
decimal_places: None,
custom_num_fmt: None,
neg_red: None,
font: None,
fill: None,
border: None,
protection: None,
alignment: None,
}
}
///|
/// Creates a Style with a custom number format.
///
/// # Parameters
/// - `format_code`: Excel number format string like "#,##0.00", "0%", "yyyy-mm-dd"
///
/// # Example
/// ```mbt nocheck
/// let currency = Style::number_format("$#,##0.00")
///
/// let percent = Style::number_format("0.0%")
///
/// let date = Style::number_format("yyyy-mm-dd")
/// ```
pub fn Style::number_format(format_code : String) -> Style {
{
number_format: Some(Custom(format_code)),
num_fmt: None,
decimal_places: None,
custom_num_fmt: None,
neg_red: None,
font: None,
fill: None,
border: None,
protection: None,
alignment: None,
}
}
///|
/// Creates a Style with a built-in number format.
///
/// # Parameters
/// - `id`: Built-in format ID (see NumberFormat for common IDs)
///
/// # Common IDs
/// - 1: 0
/// - 2: 0.00
/// - 3: #,##0
/// - 4: #,##0.00
/// - 9: 0%
/// - 10: 0.00%
/// - 14: mm-dd-yy
pub fn Style::builtin_number_format(id : Int) -> Style {
{
number_format: Some(Builtin(id)),
num_fmt: None,
decimal_places: None,
custom_num_fmt: None,
neg_red: None,
font: None,
fill: None,
border: None,
protection: None,
alignment: None,
}
}
///|
pub fn Style::excelize_custom_num_fmt(
format_code : StringView,
) -> Style raise XlsxError {
if format_code == "" {
raise InvalidOptions(msg="custom number format can not be empty")
}
Style::number_format(format_code.to_owned())
}
///|
pub fn Style::excelize_currency_num_fmt(
num_fmt : Int,
decimal_places? : Int = 2,
neg_red? : Bool = false,
) -> Style raise XlsxError {
if decimal_places < 0 || decimal_places > 30 {
raise InvalidOptions(msg="decimal places must be in [0, 30]")
}
let base = match excelize_currency_num_fmt_code(num_fmt) {
Some(v) => v
None => raise InvalidOptions(msg="unknown excelize currency number format")
}
let code = if decimal_places == 2 {
base
} else {
let mut dp = "0"
if decimal_places > 0 {
dp += "."
for _ in 0.. Style {
{
number_format: None,
num_fmt: None,
decimal_places: None,
custom_num_fmt: None,
neg_red: None,
font: Some(font),
fill: None,
border: None,
protection: None,
alignment: None,
}
}
///|
/// Returns a new Style with font settings added/replaced.
pub fn Style::with_font(self : Style, font : Font) -> Style {
{
number_format: self.number_format,
num_fmt: self.num_fmt,
decimal_places: self.decimal_places,
custom_num_fmt: self.custom_num_fmt,
neg_red: self.neg_red,
font: Some(font),
fill: self.fill,
border: self.border,
protection: self.protection,
alignment: self.alignment,
}
}
///|
/// Creates a Style with only fill settings.
pub fn Style::fill(fill : Fill) -> Style {
{
number_format: None,
num_fmt: None,
decimal_places: None,
custom_num_fmt: None,
neg_red: None,
font: None,
fill: Some(fill),
border: None,
protection: None,
alignment: None,
}
}
///|
/// Returns a new Style with fill settings added/replaced.
pub fn Style::with_fill(self : Style, fill : Fill) -> Style {
{
number_format: self.number_format,
num_fmt: self.num_fmt,
decimal_places: self.decimal_places,
custom_num_fmt: self.custom_num_fmt,
neg_red: self.neg_red,
font: self.font,
fill: Some(fill),
border: self.border,
protection: self.protection,
alignment: self.alignment,
}
}
///|
/// Creates a Style with only border settings.
///
/// # Example
/// ```mbt nocheck
/// let box_border = Style::border([
/// Border::with_values("left", color="000000", style=1),
/// Border::with_values("right", color="000000", style=1),
/// Border::with_values("top", color="000000", style=1),
/// Border::with_values("bottom", color="000000", style=1),
/// ])
/// ```
pub fn Style::border(border : Array[Border]) -> Style {
{
number_format: None,
num_fmt: None,
decimal_places: None,
custom_num_fmt: None,
neg_red: None,
font: None,
fill: None,
border: Some(border),
protection: None,
alignment: None,
}
}
///|
/// Returns a new Style with border settings added/replaced.
pub fn Style::with_border(self : Style, border : Array[Border]) -> Style {
{
number_format: self.number_format,
num_fmt: self.num_fmt,
decimal_places: self.decimal_places,
custom_num_fmt: self.custom_num_fmt,
neg_red: self.neg_red,
font: self.font,
fill: self.fill,
border: Some(border),
protection: self.protection,
alignment: self.alignment,
}
}
///|
/// Creates a Style with only protection settings.
pub fn Style::protection(protection : Protection) -> Style {
{
number_format: None,
num_fmt: None,
decimal_places: None,
custom_num_fmt: None,
neg_red: None,
font: None,
fill: None,
border: None,
protection: Some(protection),
alignment: None,
}
}
///|
fn Style::with_protection(self : Style, protection : Protection) -> Style {
{
number_format: self.number_format,
num_fmt: self.num_fmt,
decimal_places: self.decimal_places,
custom_num_fmt: self.custom_num_fmt,
neg_red: self.neg_red,
font: self.font,
fill: self.fill,
border: self.border,
protection: Some(protection),
alignment: self.alignment,
}
}
///|
pub fn Style::alignment(alignment : Alignment) -> Style {
{
number_format: None,
num_fmt: None,
decimal_places: None,
custom_num_fmt: None,
neg_red: None,
font: None,
fill: None,
border: None,
protection: None,
alignment: Some(alignment),
}
}
///|
pub fn Style::with_alignment(self : Style, alignment : Alignment) -> Style {
{
number_format: self.number_format,
num_fmt: self.num_fmt,
decimal_places: self.decimal_places,
custom_num_fmt: self.custom_num_fmt,
neg_red: self.neg_red,
font: self.font,
fill: self.fill,
border: self.border,
protection: self.protection,
alignment: Some(alignment),
}
}
///|
fn write_styles_xml(
styles : ArrayView[Style],
conditional_styles : ArrayView[Style],
default_font : StringView,
default_table_style : StringView,
default_pivot_style : StringView,
indexed_colors : Array[String]?,
mru_colors_xml : String?,
styles_ext_lst_xml : String?,
budget? : WritePartBudget,
) -> String raise XlsxError {
let style_list : Array[Style] = []
for style in styles {
style_list.push(style)
}
if style_list.length() == 0 {
style_list.push(Style::new())
}
let dxf_list : Array[Style] = []
for style in conditional_styles {
dxf_list.push(style)
}
let num_fmt_entries : Array[(Int, String)] = []
let num_fmt_ids : Map[String, Int] = Map([])
let mut next_id = 164
for style in style_list {
match style.number_format {
Some(Custom(code)) =>
match num_fmt_ids.get(code) {
Some(_) => ()
None => {
num_fmt_ids[code] = next_id
num_fmt_entries.push((next_id, code))
next_id = next_id + 1
}
}
_ => ()
}
}
for style in dxf_list {
match style.number_format {
Some(Custom(code)) =>
match num_fmt_ids.get(code) {
Some(_) => ()
None => {
num_fmt_ids[code] = next_id
num_fmt_entries.push((next_id, code))
next_id = next_id + 1
}
}
_ => ()
}
}
let sb = LimitedXmlBuilder::new(budget)
sb.write_view("\n")
sb.write_view(
"\n",
)
if num_fmt_entries.length() > 0 {
sb.write_view(" \n")
for entry in num_fmt_entries {
let (id, code) = entry
sb.write_view(" \n")
}
sb.write_view(" \n")
}
let font_name = if default_font == "" {
"Calibri"
} else {
default_font.to_owned()
}
// Font collection: id 0 is the workbook default font.
let fonts : Array[Font] = [Font::with_values(size=11.0, family=font_name)]
let font_ids : Map[String, Int] = Map([])
fn normalize_font_key(font : Font, default_family : StringView) -> String {
let family = match font.family {
Some(v) => v.to_string()
None => default_family.to_owned()
}
let size = match font.size {
Some(v) => v.to_string()
None => "11"
}
let bold = match font.bold {
Some(v) => if v { "1" } else { "0" }
None => "0"
}
let italic = match font.italic {
Some(v) => if v { "1" } else { "0" }
None => "0"
}
let strike = match font.strike {
Some(v) => if v { "1" } else { "0" }
None => "0"
}
let outline = match font.outline {
Some(v) => if v { "1" } else { "0" }
None => "0"
}
let shadow = match font.shadow {
Some(v) => if v { "1" } else { "0" }
None => "0"
}
let condense = match font.condense {
Some(v) => if v { "1" } else { "0" }
None => "0"
}
let extended = match font.extended {
Some(v) => if v { "1" } else { "0" }
None => "0"
}
let underline = match font.underline {
Some(v) => v.to_string()
None => ""
}
let color = match font.color {
Some(v) => v.to_string()
None => ""
}
let color_theme = match font.color_theme {
Some(v) => v.to_string()
None => ""
}
let color_indexed = match font.color_indexed {
Some(v) => v.to_string()
None => ""
}
let color_tint = match font.color_tint {
Some(v) => v.to_string()
None => ""
}
let charset = match font.charset {
Some(v) => v.to_string()
None => ""
}
let family_number = match font.family_number {
Some(v) => v.to_string()
None => "2"
}
let scheme = match font.scheme {
Some(v) => v.to_string()
None => ""
}
let vert_align = match font.vert_align {
Some(v) => v.to_string()
None => ""
}
"\{family}|\{size}|\{bold}|\{italic}|\{strike}|\{outline}|\{shadow}|\{condense}|\{extended}|\{underline}|\{color}|\{color_theme}|\{color_indexed}|\{color_tint}|\{charset}|\{family_number}|\{scheme}|\{vert_align}"
}
fn write_font_color(
sb : LimitedXmlBuilder,
font : Font,
) -> Unit raise XlsxError {
let tint = match font.color_tint {
Some(v) => if v == 0.0 { None } else { Some(v) }
None => None
}
match font.color_theme {
Some(value) => {
sb.write_view(" sb.write_view(" tint=\"\{v}\"")
None => ()
}
sb.write_view("/>\n")
return
}
None => ()
}
match font.color_indexed {
Some(value) => {
sb.write_view(" sb.write_view(" tint=\"\{v}\"")
None => ()
}
sb.write_view("/>\n")
return
}
None => ()
}
match font.color {
Some(value) => {
let normalized = value.replace_all(old="#", new="")
let rgb = if normalized.length() == 6 {
"FF" + normalized
} else {
normalized
}
sb.write_view(" sb.write_view(" tint=\"\{v}\"")
None => ()
}
sb.write_view("/>\n")
}
None => sb.write_view(" \n")
}
}
let default_key = normalize_font_key(fonts[0], font_name)
font_ids[default_key] = 0
for style in style_list {
match style.font {
Some(font) => {
let key = normalize_font_key(font, font_name)
if !font_ids.contains(key) {
font_ids[key] = fonts.length()
fonts.push(font)
}
}
None => ()
}
}
sb.write_view(" \n")
for font in fonts {
sb.write_view(" \n")
match font.bold {
Some(true) => sb.write_view(" \n")
_ => ()
}
match font.italic {
Some(true) => sb.write_view(" \n")
_ => ()
}
match font.strike {
Some(true) => sb.write_view(" \n")
_ => ()
}
match font.outline {
Some(true) => sb.write_view(" \n")
_ => ()
}
match font.shadow {
Some(true) => sb.write_view(" \n")
_ => ()
}
match font.condense {
Some(true) => sb.write_view(" \n")
_ => ()
}
match font.extended {
Some(true) => sb.write_view(" \n")
_ => ()
}
match font.underline {
Some(value) => {
sb.write_view(" \n")
}
None => ()
}
match font.size {
Some(value) => sb.write_view(" \n")
None => sb.write_view(" \n")
}
write_font_color(sb, font)
match font.charset {
Some(value) => sb.write_view(" \n")
None => ()
}
let family = match font.family {
Some(v) => v.to_string()
None => font_name
}
sb.write_view(" \n")
let family_number = match font.family_number {
Some(value) => value
None => 2
}
sb.write_view(" \n")
match font.vert_align {
Some(value) => {
sb.write_view(" \n")
}
None => ()
}
match font.scheme {
Some(value) => {
sb.write_view(" \n")
}
None => ()
}
sb.write_view(" \n")
}
sb.write_view(" \n")
// Fill collection: id 0/1 are the workbook default fills.
let fill_patterns : Array[String] = [
"none", "solid", "mediumGray", "darkGray", "lightGray", "darkHorizontal", "darkVertical",
"darkDown", "darkUp", "darkGrid", "darkTrellis", "lightHorizontal", "lightVertical",
"lightDown", "lightUp", "lightGrid", "lightTrellis", "gray125", "gray0625",
]
let fills : Array[String] = [
" \n", " \n",
]
let fill_budget = WritePartBudgetTracker::new(sb.remaining_budget())
for entry in fills {
fill_budget.consume(entry)
}
let fill_ids : Map[String, Int] = Map([])
fn normalize_pattern_fill_key(
pattern : Int,
fg_attrs : String,
bg_attrs : String,
) -> String {
"pattern|\{pattern}|\{fg_attrs}|\{bg_attrs}"
}
fn normalize_gradient_fill_key(
shading : Int,
c1 : String,
c2 : String,
) -> String {
"gradient|\{shading}|\{c1}|\{c2}"
}
fill_ids[normalize_pattern_fill_key(0, "", "")] = 0
fill_ids[normalize_pattern_fill_key(17, "", "")] = 1
fn clamp_transparency(v : Int) -> Int {
if v < 0 {
0
} else if v > 100 {
100
} else {
v
}
}
fn fill_rgb(color : String, transparency : Int) -> String {
let normalized = color.replace_all(old="#", new="")
let base = if normalized.length() == 8 {
normalized[2:].to_owned()
} else {
normalized
}
let t = clamp_transparency(transparency)
if t <= 0 && normalized.length() == 8 {
normalized
} else {
let opacity = 100 - t
let alpha = (opacity * 255 + 50) / 100
let a = hex_byte(alpha.to_byte())
a + base
}
}
fn fill_color_attrs(
theme : Int?,
indexed : Int?,
tint : Double?,
rgb : String?,
transparency : Int,
budget : WritePartBudget?,
) -> String raise XlsxError {
match theme {
Some(value) => {
let sb = LimitedXmlBuilder::new(budget)
sb.write_view("theme=\"\{value}\"")
match tint {
Some(v) => if v != 0.0 { sb.write_view(" tint=\"\{v}\"") }
None => ()
}
return sb.to_string()
}
None => ()
}
match indexed {
Some(value) => {
let sb = LimitedXmlBuilder::new(budget)
sb.write_view("indexed=\"\{value}\"")
match tint {
Some(v) => if v != 0.0 { sb.write_view(" tint=\"\{v}\"") }
None => ()
}
return sb.to_string()
}
None => ()
}
match rgb {
Some(value) => {
let sb = LimitedXmlBuilder::new(budget)
sb.write_view("rgb=\"")
sb.write_xml_attr(fill_rgb(value, transparency))
sb.write_view("\"")
sb.to_string()
}
None => ""
}
}
fn pattern_fill_xml(
pattern : Int,
fg_attrs : String,
bg_attrs : String,
budget : WritePartBudget?,
) -> String raise XlsxError {
let pattern_type = fill_patterns[pattern]
let sb = LimitedXmlBuilder::new(budget)
if fg_attrs == "" && bg_attrs == "" {
sb.write_view(" \n")
return sb.to_string()
}
sb.write_view(" ")
if fg_attrs != "" {
sb.write_view("")
}
if bg_attrs != "" {
sb.write_view("")
}
sb.write_view("\n")
sb.to_string()
}
fn gradient_fill_xml(
variant : GradientVariant,
c1 : String,
c2 : String,
indent : StringView,
newline : Bool,
budget : WritePartBudget?,
) -> String raise XlsxError {
let sb = LimitedXmlBuilder::new(budget)
sb.write_view(indent)
sb.write_view("")
for i, pos in variant.stops {
let color = if i == 1 { c2 } else { c1 }
sb.write_view("")
}
sb.write_view("")
if newline {
sb.write_char('\n')
}
sb.to_string()
}
for style in style_list {
match style.fill {
Some(fill) =>
match fill.typ {
Some(t) =>
if t == "pattern" {
let pattern = match fill.pattern {
Some(v) => v
None => 1
}
if pattern >= 0 && pattern < fill_patterns.length() {
let fg_rgb = match fill.colors {
Some(colors) => colors.get(0)
None => None
}
let bg_rgb = match fill.colors {
Some(colors) => colors.get(1)
None => None
}
let transparency = match fill.transparency {
Some(v) => clamp_transparency(v)
None => 0
}
let fg_attrs = fill_color_attrs(
fill.fg_theme,
fill.fg_indexed,
fill.fg_tint,
fg_rgb,
transparency,
fill_budget.remaining_budget(),
)
let bg_attrs = fill_color_attrs(
fill.bg_theme,
fill.bg_indexed,
fill.bg_tint,
bg_rgb,
transparency,
fill_budget.remaining_budget(),
)
let key = normalize_pattern_fill_key(
pattern, fg_attrs, bg_attrs,
)
if !fill_ids.contains(key) {
fill_ids[key] = fills.length()
let entry = pattern_fill_xml(
pattern,
fg_attrs,
bg_attrs,
fill_budget.remaining_budget(),
)
fill_budget.consume(entry)
fills.push(entry)
}
}
} else if t == "gradient" {
let shading = match fill.shading {
Some(v) => v
None => 0
}
if shading >= 0 && shading < fill_gradient_variants.length() {
match fill.colors {
Some(colors) =>
match (colors.get(0), colors.get(1)) {
(Some(color1), Some(color2)) => {
let transparency = match fill.transparency {
Some(v) => clamp_transparency(v)
None => 0
}
let c1 = fill_rgb(color1, transparency)
let c2 = fill_rgb(color2, transparency)
let key = normalize_gradient_fill_key(shading, c1, c2)
if !fill_ids.contains(key) {
fill_ids[key] = fills.length()
let entry = gradient_fill_xml(
fill_gradient_variants[shading],
c1,
c2,
" ",
true,
fill_budget.remaining_budget(),
)
fill_budget.consume(entry)
fills.push(entry)
}
}
_ => ()
}
None => ()
}
}
}
None => ()
}
None => ()
}
}
sb.write_view(" \n")
for entry in fills {
sb.write_view(entry)
}
sb.write_view(" \n")
// Border collection: id 0 is the workbook default border.
let border_styles : Array[String] = [
"none", "thin", "medium", "dashed", "dotted", "thick", "double", "hair", "mediumDashed",
"dashDot", "mediumDashDot", "dashDotDot", "mediumDashDotDot", "slantDashDot",
]
let borders : Array[BorderXmlEntry] = [
{
diagonal_up: false,
diagonal_down: false,
lines: [
("left", 0, None),
("right", 0, None),
("top", 0, None),
("bottom", 0, None),
("diagonal", 0, None),
("vertical", 0, None),
("horizontal", 0, None),
],
},
]
let border_ids : Map[String, Int] = Map([])
fn normalize_border_key(entry : BorderXmlEntry) -> String {
let sb = StringBuilder::new()
sb.write_view(if entry.diagonal_up { "1" } else { "0" })
sb.write_char(',')
sb.write_view(if entry.diagonal_down { "1" } else { "0" })
for line in entry.lines {
let (side, style, color) = line
sb.write_char('|')
sb.write_view(side)
sb.write_char('=')
sb.write_view(style.to_string())
sb.write_char(',')
match color {
Some(v) => sb.write_view(v)
None => ()
}
}
sb.to_string()
}
border_ids[normalize_border_key(borders[0])] = 0
fn border_lines_for_style(borders : ArrayView[Border]) -> BorderXmlEntry {
let sides : Array[String] = [
"left", "right", "top", "bottom", "vertical", "horizontal",
]
let lines : Array[(String, Int, String?)] = []
for side in sides {
let mut style = 0
let mut color : String? = None
for b in borders {
if b.typ == side {
style = match b.style {
Some(v) => v
None => 0
}
color = b.color
}
}
if style < 0 || style >= border_styles.length() {
style = 0
}
lines.push((side, style, color))
}
let mut diagonal_up = false
let mut diagonal_down = false
for b in borders {
if b.typ == "diagonalUp" {
diagonal_up = true
}
if b.typ == "diagonalDown" {
diagonal_down = true
}
}
let mut diagonal_style = 0
let mut diagonal_color : String? = None
for b in borders {
if b.typ == "diagonalUp" || b.typ == "diagonalDown" || b.typ == "diagonal" {
diagonal_style = match b.style {
Some(v) => v
None => 0
}
diagonal_color = b.color
break
}
}
if diagonal_style < 0 || diagonal_style >= border_styles.length() {
diagonal_style = 0
}
lines.push(("diagonal", diagonal_style, diagonal_color))
{ diagonal_up, diagonal_down, lines }
}
for style in style_list {
match style.border {
Some(border) => {
let entry = border_lines_for_style(border)
let key = normalize_border_key(entry)
if !border_ids.contains(key) {
border_ids[key] = borders.length()
borders.push(entry)
}
}
None => ()
}
}
sb.write_view(" \n")
for entry in borders {
sb.write_view(" ")
for line in entry.lines {
let (side, style, color) = line
if (side == "vertical" || side == "horizontal") &&
style <= 0 &&
color == None {
continue
}
if style <= 0 {
sb.write_view("<\{side}/>")
continue
}
let style_name = border_styles[style]
sb.write_view("<\{side} style=\"")
sb.write_xml_attr(style_name)
sb.write_view("\">")
match color {
Some(value) => {
let normalized = value.replace_all(old="#", new="")
let rgb = if normalized.length() == 6 {
"FF" + normalized
} else {
normalized
}
sb.write_view("")
}
None => ()
}
sb.write_view("\{side}>")
}
sb.write_view("\n")
}
sb.write_view(" \n")
let base_block =
#|
#|
#|
sb.write_view(base_block)
sb.write_view(" \n")
for style in style_list {
let (num_fmt_id, apply) = match style.number_format {
None => (0, false)
Some(Builtin(id)) => (id, true)
Some(Custom(code)) =>
match num_fmt_ids.get(code) {
Some(id) => (id, true)
None => (0, false)
}
}
let font_id = match style.font {
Some(font) =>
match font_ids.get(normalize_font_key(font, font_name)) {
Some(id) => id
None => 0
}
None => 0
}
let apply_font = font_id != 0
let fill_id = match style.fill {
Some(fill) =>
match fill.typ {
Some(t) =>
if t == "pattern" {
let pattern = match fill.pattern {
Some(v) => v
None => 1
}
let fg_rgb = match fill.colors {
Some(colors) => colors.get(0)
None => None
}
let bg_rgb = match fill.colors {
Some(colors) => colors.get(1)
None => None
}
let transparency = match fill.transparency {
Some(v) => clamp_transparency(v)
None => 0
}
let fg_attrs = fill_color_attrs(
fill.fg_theme,
fill.fg_indexed,
fill.fg_tint,
fg_rgb,
transparency,
sb.remaining_budget(),
)
let bg_attrs = fill_color_attrs(
fill.bg_theme,
fill.bg_indexed,
fill.bg_tint,
bg_rgb,
transparency,
sb.remaining_budget(),
)
match
fill_ids.get(
normalize_pattern_fill_key(pattern, fg_attrs, bg_attrs),
) {
Some(id) => id
None => 0
}
} else if t == "gradient" {
let shading = match fill.shading {
Some(v) => v
None => 0
}
if shading < 0 || shading >= fill_gradient_variants.length() {
0
} else {
match fill.colors {
Some(colors) =>
match (colors.get(0), colors.get(1)) {
(Some(color1), Some(color2)) => {
let transparency = match fill.transparency {
Some(v) => clamp_transparency(v)
None => 0
}
let c1 = fill_rgb(color1, transparency)
let c2 = fill_rgb(color2, transparency)
match
fill_ids.get(
normalize_gradient_fill_key(shading, c1, c2),
) {
Some(id) => id
None => 0
}
}
_ => 0
}
None => 0
}
}
} else {
0
}
None => 0
}
None => 0
}
let apply_fill = fill_id != 0
let border_id = match style.border {
Some(border) =>
match
border_ids.get(normalize_border_key(border_lines_for_style(border))) {
Some(id) => id
None => 0
}
None => 0
}
let apply_border = border_id != 0
let protection = style.protection
let apply_protection = match protection {
Some(p) =>
match (p.hidden, p.locked) {
(None, None) => false
_ => true
}
None => false
}
let alignment = style.alignment
let apply_alignment = match alignment {
Some(a) =>
match
(
a.horizontal,
a.vertical,
a.wrap_text,
a.text_rotation,
a.indent,
a.shrink_to_fit,
a.justify_last_line,
a.reading_order,
a.relative_indent,
) {
(None, None, None, None, None, None, None, None, None) => false
_ => true
}
None => false
}
sb.write_view(
" \n")
} else {
sb.write_view(">")
match alignment {
Some(a) =>
if apply_alignment {
sb.write_view(" {
sb.write_view(" horizontal=\"")
sb.write_xml_attr(v)
sb.write_view("\"")
}
None => ()
}
match a.vertical {
Some(v) => {
sb.write_view(" vertical=\"")
sb.write_xml_attr(v)
sb.write_view("\"")
}
None => ()
}
match a.wrap_text {
Some(v) => {
sb.write_view(" wrapText=\"")
sb.write_view(if v { "1" } else { "0" })
sb.write_view("\"")
}
None => ()
}
match a.text_rotation {
Some(v) => sb.write_view(" textRotation=\"\{v}\"")
None => ()
}
match a.indent {
Some(v) => sb.write_view(" indent=\"\{v}\"")
None => ()
}
match a.shrink_to_fit {
Some(v) => {
sb.write_view(" shrinkToFit=\"")
sb.write_view(if v { "1" } else { "0" })
sb.write_view("\"")
}
None => ()
}
match a.justify_last_line {
Some(v) => {
sb.write_view(" justifyLastLine=\"")
sb.write_view(if v { "1" } else { "0" })
sb.write_view("\"")
}
None => ()
}
match a.reading_order {
Some(v) => sb.write_view(" readingOrder=\"\{v}\"")
None => ()
}
match a.relative_indent {
Some(v) => sb.write_view(" relativeIndent=\"\{v}\"")
None => ()
}
sb.write_view("/>")
}
None => ()
}
match protection {
Some(p) => {
sb.write_view(" {
sb.write_view(" locked=\"")
sb.write_view(if v { "1" } else { "0" })
sb.write_view("\"")
}
None => ()
}
match p.hidden {
Some(v) => {
sb.write_view(" hidden=\"")
sb.write_view(if v { "1" } else { "0" })
sb.write_view("\"")
}
None => ()
}
sb.write_view("/>")
}
None => ()
}
sb.write_view("\n")
}
}
sb.write_view(" \n")
sb.write_view(" \n")
sb.write_view(" \n")
sb.write_view(" \n")
if dxf_list.length() == 0 {
sb.write_view(" \n")
} else {
sb.write_view(" \n")
for style in dxf_list {
let has_font = match style.font {
Some(font) => font != Font::new()
None => false
}
let has_fill = match style.fill {
Some(fill) =>
match fill.typ {
Some(t) => t == "pattern" || t == "gradient"
None => false
}
None => false
}
let has_border = match style.border {
Some(border) => {
let entry = border_lines_for_style(border)
let mut any = false
for line in entry.lines {
let (_, s, c) = line
if s > 0 || c != None {
any = true
}
}
any
}
None => false
}
let has_protection = match style.protection {
Some(p) =>
match (p.hidden, p.locked) {
(None, None) => false
_ => true
}
None => false
}
let has_alignment = match style.alignment {
Some(a) =>
match
(
a.horizontal,
a.vertical,
a.wrap_text,
a.text_rotation,
a.indent,
a.shrink_to_fit,
a.justify_last_line,
a.reading_order,
a.relative_indent,
) {
(None, None, None, None, None, None, None, None, None) => false
_ => true
}
None => false
}
let has_num_fmt = match style.number_format {
Some(_) => true
None => false
}
if !has_num_fmt &&
!has_font &&
!has_fill &&
!has_border &&
!has_protection &&
!has_alignment {
sb.write_view(" \n")
continue
}
sb.write_view(" ")
match style.number_format {
Some(Builtin(id)) => sb.write_view("")
Some(Custom(code)) =>
match num_fmt_ids.get(code) {
Some(id) => {
sb.write_view("")
}
None => ()
}
None => ()
}
match style.font {
Some(font) =>
if has_font {
sb.write_view("")
match font.bold {
Some(true) => sb.write_view("")
_ => ()
}
match font.italic {
Some(true) => sb.write_view("")
_ => ()
}
match font.strike {
Some(true) => sb.write_view("")
_ => ()
}
match font.outline {
Some(true) => sb.write_view("")
_ => ()
}
match font.shadow {
Some(true) => sb.write_view("")
_ => ()
}
match font.condense {
Some(true) => sb.write_view("")
_ => ()
}
match font.extended {
Some(true) => sb.write_view("")
_ => ()
}
match font.underline {
Some(value) => {
sb.write_view("")
}
None => ()
}
match font.size {
Some(value) => sb.write_view("")
None => ()
}
let tint = match font.color_tint {
Some(v) => if v == 0.0 { None } else { Some(v) }
None => None
}
match font.color_theme {
Some(value) => {
sb.write_view(" sb.write_view(" tint=\"\{v}\"")
None => ()
}
sb.write_view("/>")
}
None =>
match font.color_indexed {
Some(value) => {
sb.write_view(" sb.write_view(" tint=\"\{v}\"")
None => ()
}
sb.write_view("/>")
}
None =>
match font.color {
Some(value) => {
let normalized = value.replace_all(old="#", new="")
let rgb = if normalized.length() == 6 {
"FF" + normalized
} else {
normalized
}
sb.write_view(" sb.write_view(" tint=\"\{v}\"")
None => ()
}
sb.write_view("/>")
}
None => ()
}
}
}
match font.charset {
Some(value) => sb.write_view("")
None => ()
}
match font.family {
Some(value) => {
sb.write_view("")
}
None => ()
}
let family_number = match font.family_number {
Some(value) => value
None => 2
}
sb.write_view("")
match font.vert_align {
Some(value) => {
sb.write_view("")
}
None => ()
}
match font.scheme {
Some(value) => {
sb.write_view("")
}
None => ()
}
sb.write_view("")
}
None => ()
}
match style.fill {
Some(fill) =>
if has_fill {
match fill.typ {
Some(t) =>
if t == "pattern" {
let pattern = match fill.pattern {
Some(v) => v
None => 1
}
if pattern >= 0 && pattern < fill_patterns.length() {
let pattern_type = fill_patterns[pattern]
let transparency = match fill.transparency {
Some(v) => clamp_transparency(v)
None => 0
}
let bg_rgb = match fill.colors {
Some(colors) =>
match colors.get(1) {
Some(v) => Some(v)
None => colors.get(0)
}
None => None
}
let bg_theme = match fill.bg_theme {
Some(v) => Some(v)
None => fill.fg_theme
}
let bg_indexed = match fill.bg_indexed {
Some(v) => Some(v)
None => fill.fg_indexed
}
let bg_tint = match fill.bg_tint {
Some(v) => Some(v)
None => fill.fg_tint
}
let bg_attrs = fill_color_attrs(
bg_theme,
bg_indexed,
bg_tint,
bg_rgb,
transparency,
sb.remaining_budget(),
)
sb.write_view("")
if bg_attrs != "" {
sb.write_view("")
}
sb.write_view("")
}
} else if t == "gradient" {
let shading = match fill.shading {
Some(v) => v
None => 0
}
if shading >= 0 && shading < fill_gradient_variants.length() {
match fill.colors {
Some(colors) =>
match (colors.get(0), colors.get(1)) {
(Some(color1), Some(color2)) => {
let transparency = match fill.transparency {
Some(v) => clamp_transparency(v)
None => 0
}
let c1 = fill_rgb(color1, transparency)
let c2 = fill_rgb(color2, transparency)
sb.write_view(
gradient_fill_xml(
fill_gradient_variants[shading],
c1,
c2,
"",
false,
sb.remaining_budget(),
),
)
}
_ => ()
}
None => ()
}
}
}
None => ()
}
}
None => ()
}
match style.border {
Some(border) =>
if has_border {
let entry = border_lines_for_style(border)
sb.write_view("")
for line in entry.lines {
let (side, style, color) = line
if (side == "vertical" || side == "horizontal") &&
style <= 0 &&
color == None {
continue
}
if style <= 0 {
sb.write_view("<\{side}/>")
continue
}
let style_name = border_styles[style]
sb.write_view("<\{side} style=\"")
sb.write_xml_attr(style_name)
sb.write_view("\">")
match color {
Some(value) => {
let normalized = value.replace_all(old="#", new="")
let rgb = if normalized.length() == 6 {
"FF" + normalized
} else {
normalized
}
sb.write_view("")
}
None => ()
}
sb.write_view("\{side}>")
}
sb.write_view("")
}
None => ()
}
match style.alignment {
Some(a) =>
if has_alignment {
sb.write_view(" {
sb.write_view(" horizontal=\"")
sb.write_xml_attr(v)
sb.write_view("\"")
}
None => ()
}
match a.vertical {
Some(v) => {
sb.write_view(" vertical=\"")
sb.write_xml_attr(v)
sb.write_view("\"")
}
None => ()
}
match a.wrap_text {
Some(v) => {
sb.write_view(" wrapText=\"")
sb.write_view(if v { "1" } else { "0" })
sb.write_view("\"")
}
None => ()
}
match a.text_rotation {
Some(v) => sb.write_view(" textRotation=\"\{v}\"")
None => ()
}
match a.indent {
Some(v) => sb.write_view(" indent=\"\{v}\"")
None => ()
}
match a.shrink_to_fit {
Some(v) => {
sb.write_view(" shrinkToFit=\"")
sb.write_view(if v { "1" } else { "0" })
sb.write_view("\"")
}
None => ()
}
match a.justify_last_line {
Some(v) => {
sb.write_view(" justifyLastLine=\"")
sb.write_view(if v { "1" } else { "0" })
sb.write_view("\"")
}
None => ()
}
match a.reading_order {
Some(v) => sb.write_view(" readingOrder=\"\{v}\"")
None => ()
}
match a.relative_indent {
Some(v) => sb.write_view(" relativeIndent=\"\{v}\"")
None => ()
}
sb.write_view("/>")
}
None => ()
}
match style.protection {
Some(p) =>
if has_protection {
sb.write_view(" {
sb.write_view(" locked=\"")
sb.write_view(if v { "1" } else { "0" })
sb.write_view("\"")
}
None => ()
}
match p.hidden {
Some(v) => {
sb.write_view(" hidden=\"")
sb.write_view(if v { "1" } else { "0" })
sb.write_view("\"")
}
None => ()
}
sb.write_view("/>")
}
None => ()
}
sb.write_view("\n")
}
sb.write_view(" \n")
}
sb.write_view(" \n")
let has_indexed = match indexed_colors {
Some(colors) => colors.length() > 0
None => false
}
let has_mru = match mru_colors_xml {
Some(value) => value != ""
None => false
}
if has_indexed || has_mru {
sb.write_view(" ")
match indexed_colors {
Some(colors) =>
if colors.length() > 0 {
sb.write_view("")
for c in colors {
let normalized = normalize_rgb_hex(c).replace_all(old="#", new="")
let upper = normalized.to_upper()
let rgb = if upper.length() == 6 { "FF" + upper } else { upper }
sb.write_view("")
}
sb.write_view("")
}
None => ()
}
match mru_colors_xml {
Some(value) =>
if value != "" {
sb.write_view("")
sb.write_view(value)
sb.write_view("")
}
None => ()
}
sb.write_view("\n")
}
match styles_ext_lst_xml {
Some(value) =>
if value == "" {
sb.write_view(" \n")
} else {
sb.write_view(" ")
sb.write_view(value)
sb.write_view("\n")
}
None => ()
}
sb.write_view("")
sb.to_string()
}