///|
let max_page_break_rows = 1048575
///|
let max_page_break_cols = 16383
///|
fn normalize_optional_value(
value : StringView,
supported : Array[String],
field_name : StringView,
) -> String raise XlsxError {
let needle = value.to_owned().to_lower()
for item in supported {
if item.to_lower() == needle {
return item
}
}
raise InvalidPageLayout(msg="\{field_name.to_owned()} invalid")
}
///|
fn normalize_page_layout_options(
options : PageLayoutOptions,
) -> PageLayoutOptions raise XlsxError {
let updated = options
match updated.orientation {
Some(value) =>
updated.orientation = Some(
normalize_optional_value(
value, supported_page_orientation, "orientation",
),
)
None => ()
}
match updated.page_order {
Some(value) =>
updated.page_order = Some(
normalize_optional_value(value, supported_page_order, "page_order"),
)
None => ()
}
match updated.adjust_to {
Some(value) =>
if value < 10 || value > 400 {
raise InvalidPageLayout(msg="adjust_to out of range")
}
None => ()
}
match updated.first_page_number {
Some(value) => if value <= 0 { updated.first_page_number = None }
None => ()
}
updated
}
///|
fn check_header_footer_limit(
value : StringView,
field_name : StringView,
) -> Unit raise XlsxError {
if count_utf16_units(value) > max_field_length {
raise InvalidHeaderFooter(msg="\{field_name.to_owned()} too long")
}
}
///|
fn check_header_footer_limits(
options : HeaderFooterOptions,
) -> Unit raise XlsxError {
check_header_footer_limit(options.odd_header, "odd_header")
check_header_footer_limit(options.odd_footer, "odd_footer")
check_header_footer_limit(options.even_header, "even_header")
check_header_footer_limit(options.even_footer, "even_footer")
check_header_footer_limit(options.first_header, "first_header")
check_header_footer_limit(options.first_footer, "first_footer")
}
///|
fn remove_break_by_id(breaks : Array[PageBreak], id : Int) -> Array[PageBreak] {
let updated : Array[PageBreak] = []
for brk in breaks {
if brk.id == id {
continue
}
updated.push(brk)
}
updated
}
///|
pub fn Worksheet::set_page_margins(
self : Worksheet,
options : PageLayoutMarginsOptions?,
) -> Unit raise XlsxError {
self.ensure_stream_idle()
match options {
Some(value) => self.page_margins = Some(value)
None => ()
}
}
///|
pub fn Worksheet::set_page_layout(
self : Worksheet,
options : PageLayoutOptions?,
) -> Unit raise XlsxError {
self.ensure_stream_idle()
match options {
Some(value) => self.page_layout = Some(normalize_page_layout_options(value))
None => ()
}
}
///|
pub fn Worksheet::set_header_footer(
self : Worksheet,
options : HeaderFooterOptions?,
) -> Unit raise XlsxError {
self.ensure_stream_idle()
match options {
Some(value) => {
check_header_footer_limits(value)
self.header_footer = Some(value)
}
None => self.header_footer = None
}
}
///|
fn Worksheet::set_sheet_props(
self : Worksheet,
options : SheetPropsOptions?,
) -> Unit raise XlsxError {
self.ensure_stream_idle()
match options {
Some(value) => {
let merged = match self.sheet_props {
Some(existing) => existing
None => SheetPropsOptions::new()
}
match value.code_name {
Some(v) => merged.code_name = Some(v)
None => ()
}
match value.enable_format_conditions_calculation {
Some(v) => merged.enable_format_conditions_calculation = Some(v)
None => ()
}
match value.published {
Some(v) => merged.published = Some(v)
None => ()
}
match value.auto_page_breaks {
Some(v) => merged.auto_page_breaks = Some(v)
None => ()
}
match value.fit_to_page {
Some(v) => merged.fit_to_page = Some(v)
None => ()
}
match value.tab_color_indexed {
Some(v) => merged.tab_color_indexed = Some(v)
None => ()
}
match value.tab_color_rgb {
Some(v) => merged.tab_color_rgb = Some(v)
None => ()
}
match value.tab_color_theme {
Some(v) => merged.tab_color_theme = Some(v)
None => ()
}
match value.tab_color_tint {
Some(v) => merged.tab_color_tint = Some(v)
None => ()
}
match value.outline_summary_below {
Some(v) => merged.outline_summary_below = Some(v)
None => ()
}
match value.outline_summary_right {
Some(v) => merged.outline_summary_right = Some(v)
None => ()
}
match value.base_col_width {
Some(v) => merged.base_col_width = Some(v)
None => ()
}
match value.default_col_width {
Some(v) => merged.default_col_width = Some(v)
None => ()
}
match value.default_row_height {
Some(v) => merged.default_row_height = Some(v)
None => ()
}
match value.custom_height {
Some(v) => merged.custom_height = Some(v)
None => ()
}
match value.zero_height {
Some(v) => merged.zero_height = Some(v)
None => ()
}
match value.thick_top {
Some(v) => merged.thick_top = Some(v)
None => ()
}
match value.thick_bottom {
Some(v) => merged.thick_bottom = Some(v)
None => ()
}
if merged.default_row_height is None {
merged.default_row_height = Some(default_sheet_row_height)
}
self.sheet_props = Some(merged)
}
None => ()
}
}
///|
pub fn Worksheet::protect_sheet(
self : Worksheet,
options : SheetProtectionOptions,
) -> Unit raise XlsxError {
self.ensure_stream_idle()
self.sheet_protection = Some(sheet_protection_from_options(options))
}
///|
pub fn Worksheet::unprotect_sheet(
self : Worksheet,
password? : String = "",
) -> Unit raise XlsxError {
self.ensure_stream_idle()
match self.sheet_protection {
Some(protection) => {
if password != "" {
verify_sheet_protection_password(protection, password)
}
self.sheet_protection = None
}
None => raise InvalidSheetProtection(msg="sheet not protected")
}
}
///|
/// Returns the protection options currently applied to the worksheet,
/// mirroring Excelize's `GetSheetProtection`: stored restrictive flags are
/// inverted back into permission options, the password is never returned,
/// and an unprotected sheet yields all-default options.
pub fn Worksheet::get_sheet_protection(
self : Worksheet,
) -> SheetProtectionOptions {
let options = SheetProtectionOptions::new()
match self.sheet_protection {
Some(protection) => {
options.algorithm_name = protection.algorithm_name
options.auto_filter = !protection.auto_filter
options.delete_columns = !protection.delete_columns
options.delete_rows = !protection.delete_rows
options.edit_objects = !protection.objects
options.edit_scenarios = !protection.scenarios
options.format_cells = !protection.format_cells
options.format_columns = !protection.format_columns
options.format_rows = !protection.format_rows
options.insert_columns = !protection.insert_columns
options.insert_hyperlinks = !protection.insert_hyperlinks
options.insert_rows = !protection.insert_rows
options.pivot_tables = !protection.pivot_tables
options.select_locked_cells = !protection.select_locked_cells
options.select_unlocked_cells = !protection.select_unlocked_cells
options.sort = !protection.sort
}
None => ()
}
options
}
///|
pub fn Worksheet::set_sheet_background(
self : Worksheet,
data : Bytes,
extension : String,
) -> Unit raise XlsxError {
self.ensure_stream_idle()
if data.length() == 0 {
raise InvalidSheetBackground(msg="image data empty")
}
let ext = normalize_image_extension(extension)
let content_type = match image_content_type(ext) {
Some(value) => value
None => raise InvalidSheetBackground(msg="unsupported image type")
}
self.sheet_background = Some({ data, extension: ext, content_type })
}
///|
fn Worksheet::clear_sheet_background(self : Worksheet) -> Unit raise XlsxError {
self.ensure_stream_idle()
self.sheet_background = None
}
///|
fn Worksheet::insert_page_break(
self : Worksheet,
cell : StringView,
) -> Unit raise XlsxError {
let (row, col) = cell_ref_to_rc(cell)
// Reject an out-of-grid anchor so a page break is never stored with an id
// beyond the sheet (which would serialize an invalid ``).
if row > cell_ref_max_rows || col > cell_ref_max_cols {
raise InvalidCellRef(value=cell.to_owned())
}
let row_id = row - 1
let col_id = col - 1
if row_id == 0 && col_id == 0 {
return
}
if row_id != 0 {
let mut found = false
for brk in self.row_breaks {
if brk.id == row_id {
found = true
break
}
}
if !found {
self.row_breaks.push({
id: row_id,
min: 0,
max: max_page_break_cols,
manual: true,
})
}
}
if col_id != 0 {
let mut found = false
for brk in self.col_breaks {
if brk.id == col_id {
found = true
break
}
}
if !found {
self.col_breaks.push({
id: col_id,
min: 0,
max: max_page_break_rows,
manual: true,
})
}
}
}
///|
fn Worksheet::remove_page_break(
self : Worksheet,
cell : StringView,
) -> Unit raise XlsxError {
let (row, col) = cell_ref_to_rc(cell)
let row_id = row - 1
let col_id = col - 1
if row_id == 0 && col_id == 0 {
return
}
let row_count = self.row_breaks.length()
let col_count = self.col_breaks.length()
if row_count > 0 && col_count > 0 && row_count == col_count {
let updated_rows = remove_break_by_id(self.row_breaks, row_id)
self.row_breaks.clear()
self.row_breaks.append(updated_rows)
let updated_cols = remove_break_by_id(self.col_breaks, col_id)
self.col_breaks.clear()
self.col_breaks.append(updated_cols)
return
}
if row_count > col_count {
let updated_rows = remove_break_by_id(self.row_breaks, row_id)
self.row_breaks.clear()
self.row_breaks.append(updated_rows)
return
}
if col_count > row_count {
let updated_cols = remove_break_by_id(self.col_breaks, col_id)
self.col_breaks.clear()
self.col_breaks.append(updated_cols)
}
}