///|
fn write_row_attributes(
sb : LimitedXmlBuilder,
dim : RowDimension,
) -> Unit raise XlsxError {
match dim.height {
Some(height) => {
sb.write_view(" ht=\"")
sb.write_view(height.to_string())
sb.write_view("\" customHeight=\"1\"")
}
None => ()
}
if dim.hidden {
sb.write_view(" hidden=\"1\"")
}
if dim.outline_level > 0 {
sb.write_view(" outlineLevel=\"\{dim.outline_level}\"")
}
match dim.style_id {
Some(style_id) => sb.write_view(" s=\"\{style_id}\" customFormat=\"1\"")
None => ()
}
}
///|
fn write_col_attributes(
sb : LimitedXmlBuilder,
col : Int,
dim : ColDimension,
) -> Unit raise XlsxError {
sb.write_view(" {
sb.write_view(" width=\"")
sb.write_view(width.to_string())
sb.write_view("\" customWidth=\"1\"")
}
None => ()
}
if dim.hidden {
sb.write_view(" hidden=\"1\"")
}
if dim.outline_level > 0 {
sb.write_view(" outlineLevel=\"\{dim.outline_level}\"")
}
match dim.style_id {
Some(style_id) => sb.write_view(" style=\"\{style_id}\"")
None => ()
}
sb.write_view("/>\n")
}
///|
fn page_margin_value(value : Double?, fallback : Double) -> Double {
match value {
Some(v) => v
None => fallback
}
}
///|
fn write_print_options_xml(
options : PageLayoutMarginsOptions,
budget? : WritePartBudget,
) -> String? raise XlsxError {
let has_horizontal = options.horizontally is Some(_)
let has_vertical = options.vertically is Some(_)
if !has_horizontal && !has_vertical {
return None
}
let sb = LimitedXmlBuilder::new(budget)
sb.write_view(" sb.write_view(" horizontalCentered=\"1\"")
Some(false) => sb.write_view(" horizontalCentered=\"0\"")
None => ()
}
match options.vertically {
Some(true) => sb.write_view(" verticalCentered=\"1\"")
Some(false) => sb.write_view(" verticalCentered=\"0\"")
None => ()
}
sb.write_view("/>\n")
Some(sb.to_string())
}
///|
fn write_page_margins_xml(
options : PageLayoutMarginsOptions,
budget? : WritePartBudget,
) -> String raise XlsxError {
let sb = LimitedXmlBuilder::new(budget)
sb.write_view(" \n")
sb.to_string()
}
///|
fn write_page_setup_xml(
layout : PageLayoutOptions,
budget? : WritePartBudget,
) -> String raise XlsxError {
let sb = LimitedXmlBuilder::new(budget)
sb.write_view(" sb.write_view(" blackAndWhite=\"1\"")
Some(false) => sb.write_view(" blackAndWhite=\"0\"")
None => ()
}
match layout.fit_to_height {
Some(value) => sb.write_view(" fitToHeight=\"\{value}\"")
None => ()
}
match layout.fit_to_width {
Some(value) => sb.write_view(" fitToWidth=\"\{value}\"")
None => ()
}
match layout.size {
Some(value) => sb.write_view(" paperSize=\"\{value}\"")
None => ()
}
match layout.orientation {
Some(value) => {
sb.write_view(" orientation=\"")
sb.write_xml_attr(value)
sb.write_view("\"")
}
None => ()
}
match layout.page_order {
Some(value) => {
sb.write_view(" pageOrder=\"")
sb.write_xml_attr(value)
sb.write_view("\"")
}
None => ()
}
match layout.first_page_number {
Some(value) =>
if value > 0 {
sb.write_view(" firstPageNumber=\"\{value}\" useFirstPageNumber=\"1\"")
}
None => ()
}
match layout.adjust_to {
Some(value) => sb.write_view(" scale=\"\{value}\"")
None => ()
}
sb.write_view("/>\n")
sb.to_string()
}
///|
fn write_header_footer_xml(
options : HeaderFooterOptions,
budget? : WritePartBudget,
) -> String raise XlsxError {
let sb = LimitedXmlBuilder::new(budget)
sb.write_view(" {
let flag = if value { "1" } else { "0" }
sb.write_view(" scaleWithDoc=\"\{flag}\"")
}
None => ()
}
match options.align_with_margins {
Some(value) => {
let flag = if value { "1" } else { "0" }
sb.write_view(" alignWithMargins=\"\{flag}\"")
}
None => ()
}
sb.write_view(">\n")
if options.odd_header != "" {
sb.write_view(" ")
sb.write_xml_text(options.odd_header)
sb.write_view("\n")
}
if options.odd_footer != "" {
sb.write_view(" ")
sb.write_xml_text(options.odd_footer)
sb.write_view("\n")
}
if options.even_header != "" {
sb.write_view(" ")
sb.write_xml_text(options.even_header)
sb.write_view("\n")
}
if options.even_footer != "" {
sb.write_view(" ")
sb.write_xml_text(options.even_footer)
sb.write_view("\n")
}
if options.first_header != "" {
sb.write_view(" ")
sb.write_xml_text(options.first_header)
sb.write_view("\n")
}
if options.first_footer != "" {
sb.write_view(" ")
sb.write_xml_text(options.first_footer)
sb.write_view("\n")
}
sb.write_view(" \n")
sb.to_string()
}
///|
fn write_sheet_protection_xml(
protection : SheetProtection,
budget? : WritePartBudget,
) -> String raise XlsxError {
let sb = LimitedXmlBuilder::new(budget)
sb.write_view(" 0 {
sb.write_view(" spinCount=\"\{protection.spin_count}\"")
}
if protection.sheet {
sb.write_view(" sheet=\"1\"")
}
if protection.objects {
sb.write_view(" objects=\"1\"")
}
if protection.scenarios {
sb.write_view(" scenarios=\"1\"")
}
if protection.format_cells {
sb.write_view(" formatCells=\"1\"")
}
if protection.format_columns {
sb.write_view(" formatColumns=\"1\"")
}
if protection.format_rows {
sb.write_view(" formatRows=\"1\"")
}
if protection.insert_columns {
sb.write_view(" insertColumns=\"1\"")
}
if protection.insert_rows {
sb.write_view(" insertRows=\"1\"")
}
if protection.insert_hyperlinks {
sb.write_view(" insertHyperlinks=\"1\"")
}
if protection.delete_columns {
sb.write_view(" deleteColumns=\"1\"")
}
if protection.delete_rows {
sb.write_view(" deleteRows=\"1\"")
}
if protection.select_locked_cells {
sb.write_view(" selectLockedCells=\"1\"")
}
if protection.sort {
sb.write_view(" sort=\"1\"")
}
if protection.auto_filter {
sb.write_view(" autoFilter=\"1\"")
}
if protection.pivot_tables {
sb.write_view(" pivotTables=\"1\"")
}
if protection.select_unlocked_cells {
sb.write_view(" selectUnlockedCells=\"1\"")
}
sb.write_view("/>\n")
sb.to_string()
}
///|
fn write_page_breaks_xml(
tag_name : StringView,
breaks : ArrayView[PageBreak],
budget? : WritePartBudget,
) -> String? raise XlsxError {
if breaks.length() == 0 {
return None
}
let sb = LimitedXmlBuilder::new(budget)
let mut manual_count = 0
for brk in breaks {
if brk.manual {
manual_count = manual_count + 1
}
}
sb.write_view(
" <\{tag_name.to_owned()} count=\"\{breaks.length()}\" manualBreakCount=\"\{manual_count}\">\n",
)
for brk in breaks {
sb.write_view(" 0 {
sb.write_view(" min=\"\{brk.min}\"")
}
if brk.max > 0 {
sb.write_view(" max=\"\{brk.max}\"")
}
if brk.manual {
sb.write_view(" man=\"1\"")
}
sb.write_view("/>\n")
}
sb.write_view(" \{tag_name.to_owned()}>\n")
Some(sb.to_string())
}
///|
fn write_auto_filter_xml(
filter : AutoFilter,
budget? : WritePartBudget,
) -> String raise XlsxError {
let sb = LimitedXmlBuilder::new(budget)
let (min_row, min_col, max_row, max_col) = parse_range_ref(filter.range_ref)
let start_ref = cell_ref_from(min_row, min_col)
let end_ref = cell_ref_from(max_row, max_col)
let range_ref = "\{start_ref}:\{end_ref}"
if filter.columns.length() == 0 {
sb.write_view(" \n")
return sb.to_string()
}
sb.write_view(" \n")
for column in filter.columns {
if column.col < min_col || column.col > max_col {
raise InvalidAutoFilter(msg="auto filter column out of range")
}
let col_id = column.col - min_col
sb.write_view(" \n")
match column.filters {
Some(values) => {
sb.write_view(" \n")
for value in values {
sb.write_view(" \n")
}
sb.write_view(" \n")
}
None => ()
}
match column.custom_filters {
Some(custom) => {
sb.write_view(" \n")
for entry in custom.filters {
sb.write_view(" \n")
}
sb.write_view(" \n")
}
None => ()
}
sb.write_view(" \n")
}
sb.write_view(" \n")
sb.to_string()
}
///|
test "write worksheet layout wb: margins print options and page setup branches" {
let print_opts = PageLayoutMarginsOptions::with_values(
horizontally=false,
vertically=false,
)
let print_xml = write_print_options_xml(print_opts)
inspect(print_xml is Some(_), content="true")
match print_xml {
Some(xml) => {
inspect(xml.contains("horizontalCentered=\"0\""), content="true")
inspect(xml.contains("verticalCentered=\"0\""), content="true")
}
None => fail("expected printOptions xml")
}
let vertical_only = PageLayoutMarginsOptions::with_values(vertically=true)
let vertical_xml = write_print_options_xml(vertical_only)
inspect(vertical_xml is Some(_), content="true")
match vertical_xml {
Some(xml) => {
inspect(xml.contains("verticalCentered=\"1\""), content="true")
inspect(xml.contains("horizontalCentered="), content="false")
}
None => fail("expected vertical-only printOptions xml")
}
let margin_xml = write_page_margins_xml(PageLayoutMarginsOptions::new())
inspect(margin_xml.contains("left=\"0.7\""), content="true")
inspect(margin_xml.contains("right=\"0.7\""), content="true")
inspect(margin_xml.contains("top=\"0.75\""), content="true")
inspect(margin_xml.contains("bottom=\"0.75\""), content="true")
inspect(margin_xml.contains("header=\"0.3\""), content="true")
inspect(margin_xml.contains("footer=\"0.3\""), content="true")
let setup = PageLayoutOptions::with_values(
size=9,
first_page_number=2,
black_and_white=false,
)
let setup_xml = write_page_setup_xml(setup)
inspect(setup_xml.contains("blackAndWhite=\"0\""), content="true")
inspect(setup_xml.contains("paperSize=\"9\""), content="true")
inspect(
setup_xml.contains("firstPageNumber=\"2\" useFirstPageNumber=\"1\""),
content="true",
)
inspect(setup_xml.contains(" orientation=\""), content="false")
}
///|
test "write worksheet layout wb: header footer option branches" {
let header_footer = HeaderFooterOptions::with_values(
align_with_margins=true,
different_odd_even=true,
scale_with_doc=false,
odd_header="odd-h",
odd_footer="odd-f",
even_header="even-h",
even_footer="even-f",
first_header="first-h",
first_footer="first-f",
)
let xml = write_header_footer_xml(header_footer)
inspect(xml.contains("differentOddEven=\"1\""), content="true")
inspect(xml.contains("scaleWithDoc=\"0\""), content="true")
inspect(xml.contains("alignWithMargins=\"1\""), content="true")
inspect(xml.contains("even-h"), content="true")
}
///|
test "write worksheet layout wb: sheet protection and page breaks branches" {
let protection : SheetProtection = {
algorithm_name: "SHA-512",
password: "ABCD",
hash_value: "HASHVALUE",
salt_value: "SALTVALUE",
spin_count: 100000,
sheet: true,
objects: true,
scenarios: true,
format_cells: false,
format_columns: false,
format_rows: false,
insert_columns: false,
insert_rows: false,
insert_hyperlinks: false,
delete_columns: false,
delete_rows: false,
select_locked_cells: true,
sort: true,
auto_filter: true,
pivot_tables: false,
select_unlocked_cells: false,
}
let protection_xml = write_sheet_protection_xml(protection)
inspect(protection_xml.contains("algorithmName=\"SHA-512\""), content="true")
inspect(protection_xml.contains("hashValue=\"HASHVALUE\""), content="true")
inspect(protection_xml.contains("saltValue=\"SALTVALUE\""), content="true")
inspect(protection_xml.contains("spinCount=\"100000\""), content="true")
inspect(protection_xml.contains("autoFilter=\"1\""), content="true")
let breaks : Array[PageBreak] = [{ id: 7, min: 1, max: 10, manual: true }]
let breaks_xml = write_page_breaks_xml("rowBreaks", breaks)
inspect(breaks_xml is Some(_), content="true")
match breaks_xml {
Some(xml) => inspect(xml.contains("min=\"1\""), content="true")
None => fail("expected page breaks xml")
}
}
///|
test "write worksheet layout wb: auto-filter custom and invalid-column branches" {
let custom_filter : AutoFilter = {
range_ref: "A1:C10",
columns: [
{
col: 2,
filters: None,
custom_filters: Some({
and_filter: true,
filters: [
{ operator: "greaterThan", value: "1" },
{ operator: "lessThan", value: "9" },
],
}),
},
],
}
let custom_xml = write_auto_filter_xml(custom_filter)
inspect(custom_xml.contains(""), content="true")
let out_of_range : AutoFilter = {
range_ref: "A1:B2",
columns: [{ col: 3, filters: None, custom_filters: None }],
}
let bad : Result[String, Error] = Ok(write_auto_filter_xml(out_of_range)) catch {
e => Err(e)
}
inspect(bad is Err(XlsxError::InvalidAutoFilter(_)), content="true")
}