Skip to content

16. Freeze/Split Panes

Freeze panes lock rows and/or columns so they remain visible while scrolling.

set_panes(sheet, cell) / setPanes(sheet, cell)

Freeze rows and columns. The cell argument identifies the top-left cell of the scrollable area:

  • "A2" freezes row 1
  • "B1" freezes column A
  • "B2" freezes row 1 and column A
  • "C3" freezes rows 1-2 and columns A-B

Rust:

rust
wb.set_panes("Sheet1", "B2")?; // freeze row 1 + column A

TypeScript:

typescript
wb.setPanes("Sheet1", "B2");

unset_panes(sheet) / unsetPanes(sheet)

Remove any freeze or split panes from a sheet.

Rust:

rust
wb.unset_panes("Sheet1")?;

TypeScript:

typescript
wb.unsetPanes("Sheet1");

get_panes(sheet) / getPanes(sheet)

Get the current freeze pane cell reference, or None/null if no panes are set.

Rust:

rust
let pane: Option<String> = wb.get_panes("Sheet1")?;

TypeScript:

typescript
const pane: string | null = wb.getPanes("Sheet1");

17. Page Layout

Page layout settings control how a sheet appears when printed.

Margins

set_page_margins / setPageMargins

Set page margins in inches.

Rust:

rust
use sheetkit::page_layout::PageMarginsConfig;

wb.set_page_margins("Sheet1", &PageMarginsConfig {
    left: 0.7,
    right: 0.7,
    top: 0.75,
    bottom: 0.75,
    header: 0.3,
    footer: 0.3,
})?;

TypeScript:

typescript
wb.setPageMargins("Sheet1", {
    left: 0.7,
    right: 0.7,
    top: 0.75,
    bottom: 0.75,
    header: 0.3,
    footer: 0.3,
});

get_page_margins / getPageMargins

Get page margins for a sheet. Returns default values if not explicitly set.

Rust:

rust
let margins = wb.get_page_margins("Sheet1")?;

TypeScript:

typescript
const margins = wb.getPageMargins("Sheet1");

Page Setup

set_page_setup / setPageSetup

Set paper size, orientation, scale, and fit-to-page options.

Rust:

rust
use sheetkit::page_layout::{Orientation, PaperSize};

wb.set_page_setup("Sheet1", Some(Orientation::Landscape), Some(PaperSize::A4), Some(100), None, None)?;

TypeScript:

typescript
wb.setPageSetup("Sheet1", {
    paperSize: "a4",       // "letter" | "tabloid" | "legal" | "a3" | "a4" | "a5" | "b4" | "b5"
    orientation: "landscape",  // "portrait" | "landscape"
    scale: 100,            // 10-400
    fitToWidth: 1,         // number of pages wide
    fitToHeight: 1,        // number of pages tall
});

get_page_setup / getPageSetup

Get the current page setup for a sheet.

TypeScript:

typescript
const setup = wb.getPageSetup("Sheet1");
// { paperSize?: string, orientation?: string, scale?: number, fitToWidth?: number, fitToHeight?: number }

set_print_options / setPrintOptions

Set print options: gridlines, headings, and centering.

Rust:

rust
wb.set_print_options("Sheet1", Some(true), Some(false), Some(true), None)?;

TypeScript:

typescript
wb.setPrintOptions("Sheet1", {
    gridLines: true,
    headings: false,
    horizontalCentered: true,
    verticalCentered: false,
});

get_print_options / getPrintOptions

Get print options for a sheet.

TypeScript:

typescript
const opts = wb.getPrintOptions("Sheet1");

Set header and/or footer text for printing. Uses Excel formatting codes:

  • &L left section
  • &C center section
  • &R right section
  • &P page number
  • &N total pages
  • &D date
  • &T time
  • &F file name

Rust:

rust
wb.set_header_footer("Sheet1", Some("&CMonthly Report"), Some("&LPage &P of &N"))?;

TypeScript:

typescript
wb.setHeaderFooter("Sheet1", "&CMonthly Report", "&LPage &P of &N");

Get header and footer text for a sheet.

Rust:

rust
let (header, footer) = wb.get_header_footer("Sheet1")?;

TypeScript:

typescript
const result = wb.getHeaderFooter("Sheet1");
// { header?: string, footer?: string }

Page Breaks

insert_page_break / insertPageBreak

Insert a horizontal page break before the given 1-based row number.

Rust:

rust
wb.insert_page_break("Sheet1", 20)?;

TypeScript:

typescript
wb.insertPageBreak("Sheet1", 20);

remove_page_break / removePageBreak

Remove a page break at the given row.

Rust:

rust
wb.remove_page_break("Sheet1", 20)?;

TypeScript:

typescript
wb.removePageBreak("Sheet1", 20);

get_page_breaks / getPageBreaks

Get all row page break positions (1-based).

Rust:

rust
let breaks: Vec<u32> = wb.get_page_breaks("Sheet1")?;

TypeScript:

typescript
const breaks: number[] = wb.getPageBreaks("Sheet1");

18. Defined Names

Defined names (named ranges) assign a symbolic name to a cell reference or formula. Names can be workbook-scoped (visible from all sheets) or sheet-scoped (visible only within a specific sheet).

set_defined_name / setDefinedName

Add or update a defined name. If a name with the same name and scope already exists, its value and comment are updated (no duplication).

Rust:

rust
// Workbook-scoped name
wb.set_defined_name("SalesTotal", "Sheet1!$B$10", None, None)?;

// Sheet-scoped name with comment
wb.set_defined_name("LocalRange", "Sheet1!$A$1:$D$10", Some("Sheet1"), Some("Local data range"))?;

TypeScript:

typescript
// Workbook-scoped name
wb.setDefinedName({ name: "SalesTotal", value: "Sheet1!$B$10" });

// Sheet-scoped name with comment
wb.setDefinedName({
    name: "LocalRange",
    value: "Sheet1!$A$1:$D$10",
    scope: "Sheet1",
    comment: "Local data range",
});

get_defined_name / getDefinedName

Get a defined name by name and optional scope. Returns None/null if not found.

Rust:

rust
// Get workbook-scoped name
if let Some(info) = wb.get_defined_name("SalesTotal", None)? {
    println!("Refers to: {}", info.value);
}

// Get sheet-scoped name
if let Some(info) = wb.get_defined_name("LocalRange", Some("Sheet1"))? {
    println!("Sheet-scoped: {}", info.value);
}

TypeScript:

typescript
// Get workbook-scoped name
const info = wb.getDefinedName("SalesTotal");
if (info) {
    console.log(`Refers to: ${info.value}`);
}

// Get sheet-scoped name
const local = wb.getDefinedName("LocalRange", "Sheet1");

get_all_defined_names / getDefinedNames

List all defined names in the workbook.

Rust:

rust
let names = wb.get_all_defined_names();
for dn in &names {
    println!("{}: {} (scope: {:?})", dn.name, dn.value, dn.scope);
}

TypeScript:

typescript
const names = wb.getDefinedNames();
for (const dn of names) {
    console.log(`${dn.name}: ${dn.value} (scope: ${dn.scope ?? "workbook"})`);
}

delete_defined_name / deleteDefinedName

Delete a defined name by name and optional scope. Returns an error if the name does not exist.

Rust:

rust
wb.delete_defined_name("SalesTotal", None)?;
wb.delete_defined_name("LocalRange", Some("Sheet1"))?;

TypeScript:

typescript
wb.deleteDefinedName("SalesTotal");
wb.deleteDefinedName("LocalRange", "Sheet1");

DefinedNameInfo

FieldRust TypeTypeScript TypeDescription
nameStringstringThe defined name
valueStringstringThe reference or formula
scopeDefinedNameScopestring?Sheet name if sheet-scoped, or None/undefined if workbook-scoped
commentOption<String>string?Optional comment

Defined names cannot contain \ / ? * [ ] characters and cannot start or end with whitespace.


19. Document Properties

Document properties store metadata about the workbook file.

Core Properties

set_doc_props / setDocProps

Set core document properties (title, creator, etc.).

Rust:

rust
use sheetkit::doc_props::DocProperties;

wb.set_doc_props(DocProperties {
    title: Some("Annual Report".to_string()),
    creator: Some("Finance Team".to_string()),
    subject: Some("Financial Summary".to_string()),
    ..Default::default()
});

TypeScript:

typescript
wb.setDocProps({
    title: "Annual Report",
    creator: "Finance Team",
    subject: "Financial Summary",
});

get_doc_props / getDocProps

Get core document properties.

Rust:

rust
let props = wb.get_doc_props();

TypeScript:

typescript
const props = wb.getDocProps();

Core Properties Fields

FieldTypeDescription
titleOption<String> / string?Document title
subjectOption<String> / string?Subject
creatorOption<String> / string?Author name
keywordsOption<String> / string?Keywords
descriptionOption<String> / string?Description/comments
last_modified_byOption<String> / string?Last editor
revisionOption<String> / string?Revision number
createdOption<String> / string?Creation timestamp (ISO 8601)
modifiedOption<String> / string?Last modified timestamp
categoryOption<String> / string?Category
content_statusOption<String> / string?Content status

Application Properties

set_app_props / setAppProps

Set application properties.

Rust:

rust
use sheetkit::doc_props::AppProperties;

wb.set_app_props(AppProperties {
    application: Some("SheetKit".to_string()),
    company: Some("Acme Corp".to_string()),
    ..Default::default()
});

TypeScript:

typescript
wb.setAppProps({
    application: "SheetKit",
    company: "Acme Corp",
});

get_app_props / getAppProps

Get application properties.

Application Properties Fields

FieldTypeDescription
applicationOption<String> / string?Application name
doc_securityOption<u32> / number?Document security level
companyOption<String> / string?Company name
app_versionOption<String> / string?Application version
managerOption<String> / string?Manager name
templateOption<String> / string?Template name

Custom Properties

Custom properties store arbitrary key-value metadata.

set_custom_property / setCustomProperty

Set a custom property. Accepts String, Int, Float, Bool, or DateTime values.

Rust:

rust
use sheetkit::doc_props::CustomPropertyValue;

wb.set_custom_property("Department", CustomPropertyValue::String("Engineering".to_string()));
wb.set_custom_property("Version", CustomPropertyValue::Int(3));
wb.set_custom_property("Approved", CustomPropertyValue::Bool(true));

TypeScript:

typescript
wb.setCustomProperty("Department", "Engineering");
wb.setCustomProperty("Version", 3);
wb.setCustomProperty("Approved", true);

Note: In TypeScript, numeric values are automatically distinguished as integer or float. Integer-like numbers (no fractional part) within the i32 range are stored as Int; others are stored as Float.

get_custom_property / getCustomProperty

Get a custom property value, or None/null if not found.

Rust:

rust
if let Some(value) = wb.get_custom_property("Department") {
    // value is CustomPropertyValue
}

TypeScript:

typescript
const value = wb.getCustomProperty("Department");
// string | number | boolean | null

delete_custom_property / deleteCustomProperty

Delete a custom property. Returns true if it existed.

Rust:

rust
let existed: bool = wb.delete_custom_property("Department");

TypeScript:

typescript
const existed: boolean = wb.deleteCustomProperty("Department");

20. Workbook Protection

Workbook protection prevents structural changes to the workbook (adding, removing, or renaming sheets).

protect_workbook / protectWorkbook

Protect the workbook with optional password and lock settings.

Rust:

rust
use sheetkit::protection::WorkbookProtectionConfig;

wb.protect_workbook(WorkbookProtectionConfig {
    password: Some("secret".to_string()),
    lock_structure: true,
    lock_windows: false,
    lock_revision: false,
});

TypeScript:

typescript
wb.protectWorkbook({
    password: "secret",
    lockStructure: true,
    lockWindows: false,
    lockRevision: false,
});

unprotect_workbook / unprotectWorkbook

Remove workbook protection.

Rust:

rust
wb.unprotect_workbook();

TypeScript:

typescript
wb.unprotectWorkbook();

is_workbook_protected / isWorkbookProtected

Check whether the workbook is protected.

Rust:

rust
let protected: bool = wb.is_workbook_protected();

TypeScript:

typescript
const isProtected: boolean = wb.isWorkbookProtected();

WorkbookProtectionConfig

FieldTypeDescription
passwordOption<String> / string?Password (hashed with legacy Excel algorithm)
lock_structurebool / boolean?Prevent adding/removing/renaming sheets
lock_windowsbool / boolean?Prevent moving/resizing workbook windows
lock_revisionbool / boolean?Lock revision tracking

Note: The password uses the legacy Excel hash algorithm, which is NOT cryptographically secure. It provides only basic deterrence.


21. Sheet Protection

Sheet protection prevents editing of cells within a single sheet. You can optionally specify a password and grant specific permissions.

protect_sheet / protectSheet

Protect a sheet with optional password and granular permission settings. All permission booleans default to false (forbidden). Set a permission to true to allow that action even when the sheet is protected.

Rust:

rust
use sheetkit::sheet::SheetProtectionConfig;

wb.protect_sheet("Sheet1", &SheetProtectionConfig {
    password: Some("mypass".to_string()),
    format_cells: true,
    insert_rows: true,
    sort: true,
    ..SheetProtectionConfig::default()
})?;

TypeScript:

typescript
wb.protectSheet("Sheet1", {
    password: "mypass",
    formatCells: true,
    insertRows: true,
    sort: true,
});

// Protect with defaults (all actions forbidden, no password)
wb.protectSheet("Sheet1");

unprotect_sheet / unprotectSheet

Remove protection from a sheet.

Rust:

rust
wb.unprotect_sheet("Sheet1")?;

TypeScript:

typescript
wb.unprotectSheet("Sheet1");

is_sheet_protected / isSheetProtected

Check if a sheet is protected.

Rust:

rust
let protected: bool = wb.is_sheet_protected("Sheet1")?;

TypeScript:

typescript
const isProtected: boolean = wb.isSheetProtected("Sheet1");

SheetProtectionConfig

FieldRust TypeTypeScript TypeDefaultDescription
passwordOption<String>string?NonePassword (hashed with legacy Excel algorithm)
select_locked_cells / selectLockedCellsboolboolean?falseAllow selecting locked cells
select_unlocked_cells / selectUnlockedCellsboolboolean?falseAllow selecting unlocked cells
format_cells / formatCellsboolboolean?falseAllow formatting cells
format_columns / formatColumnsboolboolean?falseAllow formatting columns
format_rows / formatRowsboolboolean?falseAllow formatting rows
insert_columns / insertColumnsboolboolean?falseAllow inserting columns
insert_rows / insertRowsboolboolean?falseAllow inserting rows
insert_hyperlinks / insertHyperlinksboolboolean?falseAllow inserting hyperlinks
delete_columns / deleteColumnsboolboolean?falseAllow deleting columns
delete_rows / deleteRowsboolboolean?falseAllow deleting rows
sortboolboolean?falseAllow sorting
auto_filter / autoFilterboolboolean?falseAllow using auto-filter
pivot_tables / pivotTablesboolboolean?falseAllow using pivot tables

Note: The password uses the legacy Excel hash algorithm, which is NOT cryptographically secure. It provides only basic deterrence.


22. Formula Evaluation

SheetKit includes a formula evaluator that supports 164 Excel functions. Formulas are parsed using a nom-based parser and evaluated against the current workbook data.

set_cell_formula / setCellFormula

Set a formula on a single cell.

Rust:

rust
wb.set_cell_formula("Sheet1", "C1", "SUM(A1:B1)")?;

TypeScript:

typescript
wb.setCellFormula("Sheet1", "C1", "SUM(A1:B1)");

fill_formula / fillFormula

Fill a single-column range with a formula, automatically adjusting row references for each row. Absolute row references ($1) are not adjusted. The first cell in the range gets the formula as-is; subsequent cells have their row references shifted by the row offset.

Rust:

rust
// Sets D2 = SUM(A2:C2), D3 = SUM(A3:C3), ..., D10 = SUM(A10:C10)
wb.fill_formula("Sheet1", "D2:D10", "SUM(A2:C2)")?;

// Absolute references are preserved:
// E2 = $A$1*B2, E3 = $A$1*B3, E4 = $A$1*B4
wb.fill_formula("Sheet1", "E2:E4", "$A$1*B2")?;

TypeScript:

typescript
wb.fillFormula("Sheet1", "D2:D10", "SUM(A2:C2)");
wb.fillFormula("Sheet1", "E2:E4", "$A$1*B2");

Note: Only single-column ranges are supported (e.g., "D2:D10"). Multi-column ranges return an error.

evaluate_formula / evaluateFormula

Evaluate a single formula string in the context of a specific sheet.

Rust:

rust
let result: CellValue = wb.evaluate_formula("Sheet1", "SUM(A1:A10)")?;

TypeScript:

typescript
const result = wb.evaluateFormula("Sheet1", "SUM(A1:A10)");
// returns: string | number | boolean | DateValue | null

calculate_all / calculateAll

Recalculate all formula cells in the workbook. Uses a dependency graph with topological sort (Kahn's algorithm) to ensure formulas are calculated in the correct order.

Rust:

rust
wb.calculate_all()?;

TypeScript:

typescript
wb.calculateAll();

Supported Functions (164)

Math (23 functions)

SUM, ABS, INT, ROUND, ROUNDUP, ROUNDDOWN, MOD, POWER, SQRT, CEILING, FLOOR, SIGN, RAND, RANDBETWEEN, PI, LOG, LOG10, LN, EXP, PRODUCT, QUOTIENT, FACT, SUMIF, SUMIFS

Statistical (15 functions)

AVERAGE, COUNT, COUNTA, MIN, MAX, AVERAGEIF, AVERAGEIFS, COUNTBLANK, COUNTIF, COUNTIFS, MEDIAN, MODE, LARGE, SMALL, RANK

Text (18 functions)

LEN, LOWER, UPPER, TRIM, LEFT, RIGHT, MID, CONCATENATE, CONCAT, FIND, SEARCH, SUBSTITUTE, REPLACE, REPT, EXACT, T, PROPER, VALUE, TEXT

Logical (11 functions)

IF, AND, OR, NOT, TRUE, FALSE, IFERROR, IFNA, IFS, SWITCH, XOR

Information (13 functions)

ISNUMBER, ISTEXT, ISBLANK, ISERROR, ISERR, ISNA, ISLOGICAL, ISEVEN, ISODD, TYPE, N, NA, ERROR.TYPE

Date/Time (17 functions)

DATE, TODAY, NOW, YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, DATEDIF, EDATE, EOMONTH, DATEVALUE, WEEKDAY, WEEKNUM, NETWORKDAYS, WORKDAY

Lookup (11 functions)

VLOOKUP, HLOOKUP, INDEX, MATCH, LOOKUP, ROW, COLUMN, ROWS, COLUMNS, CHOOSE, ADDRESS

Financial (21 functions)

FV, PV, NPV, IRR, PMT, IPMT, PPMT, RATE, NPER, DB, DDB, SLN, SYD, EFFECT, NOMINAL, DOLLARDE, DOLLARFR, CUMIPMT, CUMPRINC, XNPV, XIRR

Engineering (33 functions)

BIN2DEC, BIN2HEX, BIN2OCT, DEC2BIN, DEC2HEX, DEC2OCT, HEX2BIN, HEX2DEC, HEX2OCT, OCT2BIN, OCT2DEC, OCT2HEX, DELTA, GESTEP, ERF, ERFC, COMPLEX, IMREAL, IMAGINARY, IMABS, IMARGUMENT, IMCONJUGATE, IMSUM, IMSUB, IMPRODUCT, IMDIV, IMPOWER, IMSQRT, CONVERT, BESSELI, BESSELJ, BESSELK, BESSELY

Note: Function names are case-insensitive. Unsupported functions return an error. The evaluator supports cell references (A1, $B$2), range references (A1:C10), cross-sheet references (Sheet2!A1), and standard arithmetic operators (+, -, *, /, ^, &, comparison operators).


23. Pivot Tables

Pivot tables summarize data from a source range into a structured report.

add_pivot_table / addPivotTable

Add a pivot table to the workbook.

Rust:

rust
use sheetkit::pivot::{PivotTableConfig, PivotField, PivotDataField, AggregateFunction};

let config = PivotTableConfig {
    name: "SalesPivot".to_string(),
    source_sheet: "Data".to_string(),
    source_range: "A1:D100".to_string(),
    target_sheet: "PivotSheet".to_string(),
    target_cell: "A1".to_string(),
    rows: vec![PivotField { name: "Region".to_string() }],
    columns: vec![PivotField { name: "Quarter".to_string() }],
    data: vec![PivotDataField {
        name: "Revenue".to_string(),
        function: AggregateFunction::Sum,
        display_name: Some("Total Revenue".to_string()),
    }],
};
wb.add_pivot_table(&config)?;

TypeScript:

typescript
wb.addPivotTable({
    name: "SalesPivot",
    sourceSheet: "Data",
    sourceRange: "A1:D100",
    targetSheet: "PivotSheet",
    targetCell: "A1",
    rows: [{ name: "Region" }],
    columns: [{ name: "Quarter" }],
    data: [{
        name: "Revenue",
        function: "sum",
        displayName: "Total Revenue",
    }],
});

Note (Node.js): each data[].function must be a supported aggregate (sum, count, average, max, min, product, countNums, stdDev, stdDevP, var, varP). Unknown values return an error.

get_pivot_tables / getPivotTables

Get all pivot tables in the workbook.

Rust:

rust
let tables: Vec<PivotTableInfo> = wb.get_pivot_tables();
for t in &tables {
    println!("{}: {} -> {}", t.name, t.source_range, t.location);
}

TypeScript:

typescript
const tables = wb.getPivotTables();

delete_pivot_table / deletePivotTable

Delete a pivot table by name.

Rust:

rust
wb.delete_pivot_table("SalesPivot")?;

TypeScript:

typescript
wb.deletePivotTable("SalesPivot");

PivotTableConfig

FieldTypeDescription
nameString / stringPivot table name
source_sheetString / stringSource data sheet name
source_rangeString / stringSource data range (e.g., "A1:D100")
target_sheetString / stringTarget sheet for the pivot table
target_cellString / stringTop-left cell of the pivot table
rowsVec<PivotField> / PivotField[]Row fields
columnsVec<PivotField> / PivotField[]Column fields
dataVec<PivotDataField> / PivotDataField[]Data/value fields

PivotDataField

FieldTypeDescription
nameString / stringColumn name from source data header
functionAggregateFunction / stringAggregate function
display_nameOption<String> / string?Custom display name

Aggregate Functions

RustTypeScriptDescription
AggregateFunction::Sum"sum"Sum of values
AggregateFunction::Count"count"Count of entries
AggregateFunction::Average"average"Average
AggregateFunction::Max"max"Maximum
AggregateFunction::Min"min"Minimum
AggregateFunction::Product"product"Product
AggregateFunction::CountNums"countNums"Count of numeric values
AggregateFunction::StdDev"stdDev"Standard deviation (sample)
AggregateFunction::StdDevP"stdDevP"Standard deviation (population)
AggregateFunction::Var"var"Variance (sample)
AggregateFunction::VarP"varP"Variance (population)

24. StreamWriter

The StreamWriter provides a forward-only streaming API for writing large sheets without holding the entire worksheet in memory. Rows must be written in ascending order.

Basic Workflow

  1. Create a stream writer from the workbook
  2. Set column widths and other column settings (must be done BEFORE writing any rows)
  3. Write rows in ascending order
  4. Apply the stream writer back to the workbook

Rust:

rust
use sheetkit::cell::CellValue;

let mut sw = wb.new_stream_writer("LargeSheet")?;

// Set column widths BEFORE writing rows
sw.set_col_width(1, 20.0)?;
sw.set_col_width(2, 15.0)?;

// Write header
sw.write_row(1, &[
    CellValue::from("Name"),
    CellValue::from("Score"),
])?;

// Write data rows
for i in 2..=1000 {
    sw.write_row(i, &[
        CellValue::from(format!("Item {}", i - 1)),
        CellValue::from(i as f64 * 1.5),
    ])?;
}

// Apply to workbook
let sheet_index = wb.apply_stream_writer(sw)?;
wb.save("large_output.xlsx")?;

TypeScript:

typescript
const sw = wb.newStreamWriter("LargeSheet");

// Set column widths BEFORE writing rows
sw.setColWidth(1, 20.0);
sw.setColWidth(2, 15.0);

// Write header
sw.writeRow(1, ["Name", "Score"]);

// Write data rows
for (let i = 2; i <= 1000; i++) {
    sw.writeRow(i, [`Item ${i - 1}`, i * 1.5]);
}

// Apply to workbook
const sheetIndex = wb.applyStreamWriter(sw);
await wb.save("large_output.xlsx");

StreamWriter API

set_col_width(col, width) / setColWidth(col, width)

Set the width of a single column. Column numbers are 1-based. Must be called before any write_row.

set_col_width_range(min, max, width) / setColWidthRange(min, max, width)

Set the width for a range of columns (inclusive). Must be called before any write_row.

write_row(row, values) / writeRow(row, values)

Write a row of values. Row numbers are 1-based and must be written in ascending order.

add_merge_cell(reference) / addMergeCell(reference)

Register a merge cell range (e.g., "A1:C1").

Rust:

rust
sw.add_merge_cell("A1:C1")?;

TypeScript:

typescript
sw.addMergeCell("A1:C1");

Rust-Only StreamWriter Methods

The following methods are available only in the Rust API:

  • set_freeze_panes(cell) -- Set freeze panes for the streamed sheet (must be called before writing rows)
  • set_col_visible(col, visible) -- Set column visibility
  • set_col_outline_level(col, level) -- Set column outline level (0-7)
  • set_col_style(col, style_id) -- Set column style
  • write_row_with_options(row, values, options) -- Write a row with custom options (height, visibility, outline level, style)
rust
use sheetkit::stream::StreamRowOptions;

sw.set_freeze_panes("A2")?; // freeze row 1
sw.set_col_visible(3, false)?; // hide column C
sw.set_col_style(1, style_id)?;

sw.write_row_with_options(1, &values, &StreamRowOptions {
    height: Some(25.0),
    visible: Some(true),
    outline_level: Some(1),
    style_id: Some(style_id),
})?;

Important: Column widths, visibility, styles, outline levels, and freeze panes must ALL be set before the first write_row call. Setting them after writing any rows returns an error.


25. Utility Functions

These utility functions are available in the Rust API only (sheetkit_core::utils::cell_ref).

cell_name_to_coordinates

Convert an A1-style cell reference to 1-based (column, row) coordinates. Supports absolute references (e.g., "$B$3").

rust
use sheetkit_core::utils::cell_ref::cell_name_to_coordinates;

let (col, row) = cell_name_to_coordinates("B3")?;
assert_eq!((col, row), (2, 3));

let (col, row) = cell_name_to_coordinates("$AA$100")?;
assert_eq!((col, row), (27, 100));

coordinates_to_cell_name

Convert 1-based (column, row) coordinates to an A1-style cell reference.

rust
use sheetkit_core::utils::cell_ref::coordinates_to_cell_name;

let name = coordinates_to_cell_name(2, 3)?;
assert_eq!(name, "B3");

column_name_to_number

Convert a column letter name to a 1-based column number.

rust
use sheetkit_core::utils::cell_ref::column_name_to_number;

assert_eq!(column_name_to_number("A")?, 1);
assert_eq!(column_name_to_number("Z")?, 26);
assert_eq!(column_name_to_number("AA")?, 27);
assert_eq!(column_name_to_number("XFD")?, 16384);

column_number_to_name

Convert a 1-based column number to its letter name.

rust
use sheetkit_core::utils::cell_ref::column_number_to_name;

assert_eq!(column_number_to_name(1)?, "A");
assert_eq!(column_number_to_name(26)?, "Z");
assert_eq!(column_number_to_name(27)?, "AA");
assert_eq!(column_number_to_name(16384)?, "XFD");

Date Conversion Functions

Available in sheetkit_core::cell:

  • date_to_serial(NaiveDate) -> f64 -- Convert a chrono date to an Excel serial number
  • datetime_to_serial(NaiveDateTime) -> f64 -- Convert a chrono datetime to an Excel serial number with time fraction
  • serial_to_date(f64) -> Option<NaiveDate> -- Convert an Excel serial number to a date
  • serial_to_datetime(f64) -> Option<NaiveDateTime> -- Convert an Excel serial number to a datetime
rust
use chrono::NaiveDate;
use sheetkit_core::cell::{date_to_serial, serial_to_date};

let date = NaiveDate::from_ymd_opt(2025, 6, 15).unwrap();
let serial = date_to_serial(date);
let roundtrip = serial_to_date(serial).unwrap();
assert_eq!(date, roundtrip);

Note: Excel uses the 1900 date system with a known bug where it incorrectly treats 1900 as a leap year. Serial number 60 (February 29, 1900) does not correspond to a real date. These conversion functions account for this bug.

is_date_num_fmt(num_fmt_id) (Rust only)

Check whether a built-in number format ID represents a date or time format. Returns true for IDs 14-22 and 45-47.

rust
use sheetkit::is_date_num_fmt;

assert!(is_date_num_fmt(14));   // m/d/yyyy
assert!(is_date_num_fmt(22));   // m/d/yyyy h:mm
assert!(!is_date_num_fmt(0));   // General
assert!(!is_date_num_fmt(49));  // @

is_date_format_code(code) (Rust only)

Check whether a custom number format string represents a date or time format. Returns true if the format code contains date/time tokens (y, m, d, h, s) outside of quoted strings and escaped characters.

rust
use sheetkit::is_date_format_code;

assert!(is_date_format_code("yyyy-mm-dd"));
assert!(is_date_format_code("h:mm:ss AM/PM"));
assert!(!is_date_format_code("#,##0.00"));
assert!(!is_date_format_code("0%"));

26. Sparklines

Sparklines are mini-charts embedded in worksheet cells. SheetKit supports three sparkline types: Line, Column, and Win/Loss. Excel defines 36 style presets (indices 0-35).

Sparklines are stored as x14 worksheet extensions in the OOXML package and persist through save/open roundtrips.

Types

SparklineType (Rust) / sparklineType (TypeScript)

ValueRustTypeScriptOOXML
LineSparklineType::Line"line"(default, omitted)
ColumnSparklineType::Column"column""column"
Win/LossSparklineType::WinLoss"winloss" or "stacked""stacked"

SparklineConfig (Rust)

rust
use sheetkit::SparklineConfig;

let config = SparklineConfig::new("Sheet1!A1:A10", "B1");

Fields:

FieldTypeDefaultDescription
data_rangeString(required)Data source range (e.g., "Sheet1!A1:A10")
locationString(required)Cell where sparkline is rendered (e.g., "B1")
sparkline_typeSparklineTypeLineSparkline chart type
markersboolfalseShow data markers
high_pointboolfalseHighlight highest point
low_pointboolfalseHighlight lowest point
first_pointboolfalseHighlight first point
last_pointboolfalseHighlight last point
negative_pointsboolfalseHighlight negative values
show_axisboolfalseShow horizontal axis
line_weightOption<f64>NoneLine weight in points
styleOption<u32>NoneStyle preset index (0-35)

JsSparklineConfig (TypeScript)

typescript
const config = {
  dataRange: 'Sheet1!A1:A10',
  location: 'B1',
  sparklineType: 'line',    // "line" | "column" | "winloss" | "stacked"
  markers: true,
  highPoint: false,
  lowPoint: false,
  firstPoint: false,
  lastPoint: false,
  negativePoints: false,
  showAxis: false,
  lineWeight: 0.75,
  style: 1,
};

Workbook.addSparkline / Workbook::add_sparkline

Add a sparkline to a worksheet.

Rust:

rust
use sheetkit::{Workbook, SparklineConfig, SparklineType};

let mut wb = Workbook::new();

let mut config = SparklineConfig::new("Sheet1!A1:A10", "B1");
config.sparkline_type = SparklineType::Column;
config.markers = true;
config.high_point = true;

wb.add_sparkline("Sheet1", &config).unwrap();

TypeScript:

typescript
import { Workbook } from '@sheetkit/node';

const wb = new Workbook();
wb.addSparkline('Sheet1', {
  dataRange: 'Sheet1!A1:A10',
  location: 'B1',
  sparklineType: 'column',
  markers: true,
  highPoint: true,
});

Workbook.getSparklines / Workbook::get_sparklines

Retrieve all sparklines for a worksheet.

Rust:

rust
let sparklines = wb.get_sparklines("Sheet1").unwrap();
for s in &sparklines {
    println!("{} -> {}", s.data_range, s.location);
}

TypeScript:

typescript
const sparklines = wb.getSparklines('Sheet1');
for (const s of sparklines) {
  console.log(`${s.dataRange} -> ${s.location}`);
}

Workbook.removeSparkline / Workbook::remove_sparkline

Remove a sparkline by its location cell reference.

Rust:

rust
wb.remove_sparkline("Sheet1", "B1").unwrap();

TypeScript:

typescript
wb.removeSparkline('Sheet1', 'B1');

Validation

The validate_sparkline_config function (Rust) checks that:

  • data_range is not empty
  • location is not empty
  • line_weight (if set) is positive
  • style (if set) is in range 0-35

Validation is automatically applied when calling add_sparkline.

rust
use sheetkit_core::sparkline::{SparklineConfig, validate_sparkline_config};

let config = SparklineConfig::new("Sheet1!A1:A10", "B1");
validate_sparkline_config(&config).unwrap(); // Ok

27. Theme Colors

Resolve theme color slots (dk1, lt1, dk2, lt2, accent1-6, hlink, folHlink) with optional tint.

Workbook.getThemeColor (Node.js) / Workbook::get_theme_color (Rust)

ParameterTypeDescription
indexu32 / numberTheme color index (0-11)
tintOption<f64> / number | nullTint value: positive lightens, negative darkens

Returns: ARGB hex string (e.g. "FF4472C4") or None/null if out of range.

Theme Color Indices:

IndexSlot NameDefault Color
0dk1FF000000
1lt1FFFFFFFF
2dk2FF44546A
3lt2FFE7E6E6
4accent1FF4472C4
5accent2FFED7D31
6accent3FFA5A5A5
7accent4FFFFC000
8accent5FF5B9BD5
9accent6FF70AD47
10hlinkFF0563C1
11folHlinkFF954F72

Node.js

javascript
const wb = new Workbook();

// Get accent1 color (no tint)
const color = wb.getThemeColor(4, null); // "FF4472C4"

// Lighten black by 50%
const lightened = wb.getThemeColor(0, 0.5); // "FF7F7F7F"

// Darken white by 50%
const darkened = wb.getThemeColor(1, -0.5); // "FF7F7F7F"

// Out of range returns null
const invalid = wb.getThemeColor(99, null); // null

Rust

rust
let wb = Workbook::new();

// Get accent1 color (no tint)
let color = wb.get_theme_color(4, None); // Some("FF4472C4")

// Apply tint
let tinted = wb.get_theme_color(0, Some(0.5)); // Some("FF7F7F7F")

Gradient Fill

The FillStyle type supports gradient fills via the gradient field.

Types

rust
pub struct GradientFillStyle {
    pub gradient_type: GradientType, // Linear or Path
    pub degree: Option<f64>,         // Rotation angle for linear gradients
    pub left: Option<f64>,           // Path gradient coordinates (0.0-1.0)
    pub right: Option<f64>,
    pub top: Option<f64>,
    pub bottom: Option<f64>,
    pub stops: Vec<GradientStop>,    // Color stops
}

pub struct GradientStop {
    pub position: f64,     // Position (0.0-1.0)
    pub color: StyleColor, // Color at this stop
}

pub enum GradientType {
    Linear,
    Path,
}

Rust Example

rust
use sheetkit::*;

let mut wb = Workbook::new();
let style_id = wb.add_style(&Style {
    fill: Some(FillStyle {
        pattern: PatternType::None,
        fg_color: None,
        bg_color: None,
        gradient: Some(GradientFillStyle {
            gradient_type: GradientType::Linear,
            degree: Some(90.0),
            left: None,
            right: None,
            top: None,
            bottom: None,
            stops: vec![
                GradientStop {
                    position: 0.0,
                    color: StyleColor::Rgb("FFFFFFFF".to_string()),
                },
                GradientStop {
                    position: 1.0,
                    color: StyleColor::Rgb("FF4472C4".to_string()),
                },
            ],
        }),
    }),
    ..Style::default()
})?;

28. Rich Text

Rich text allows a single cell to contain multiple text segments (runs), each with independent formatting such as font, size, bold, italic, and color.

RichTextRun Type

Each run in a rich text cell is described by a RichTextRun.

Rust:

rust
pub struct RichTextRun {
    pub text: String,
    pub font: Option<String>,
    pub size: Option<f64>,
    pub bold: bool,
    pub italic: bool,
    pub color: Option<String>,
}

TypeScript:

typescript
interface RichTextRun {
  text: string;
  font?: string;
  size?: number;
  bold?: boolean;
  italic?: boolean;
  color?: string;  // RGB hex string, e.g. "#FF0000"
}

set_cell_rich_text / setCellRichText

Set a cell value to rich text with multiple formatted runs.

Rust:

rust
use sheetkit::{Workbook, RichTextRun};

let mut wb = Workbook::new();
let runs = vec![
    RichTextRun {
        text: "Bold text".to_string(),
        font: Some("Arial".to_string()),
        size: Some(14.0),
        bold: true,
        italic: false,
        color: Some("#FF0000".to_string()),
    },
    RichTextRun {
        text: " normal text".to_string(),
        font: None,
        size: None,
        bold: false,
        italic: false,
        color: None,
    },
];
wb.set_cell_rich_text("Sheet1", "A1", runs)?;

TypeScript:

typescript
const wb = new Workbook();
wb.setCellRichText("Sheet1", "A1", [
  { text: "Bold text", font: "Arial", size: 14, bold: true, color: "#FF0000" },
  { text: " normal text" },
]);

get_cell_rich_text / getCellRichText

Retrieve the rich text runs for a cell. Returns None/null for non-rich-text cells.

Rust:

rust
let runs = wb.get_cell_rich_text("Sheet1", "A1")?;
if let Some(runs) = runs {
    for run in &runs {
        println!("Text: {:?}, Bold: {}", run.text, run.bold);
    }
}

TypeScript:

typescript
const runs = wb.getCellRichText("Sheet1", "A1");
if (runs) {
  for (const run of runs) {
    console.log(`Text: ${run.text}, Bold: ${run.bold ?? false}`);
  }
}

CellValue::RichString (Rust only)

Rich text cells use the CellValue::RichString(Vec<RichTextRun>) variant. When read through get_cell_value, the display value is the concatenation of all run texts.

rust
match wb.get_cell_value("Sheet1", "A1")? {
    CellValue::RichString(runs) => {
        println!("Rich text with {} runs", runs.len());
    }
    _ => {}
}

rich_text_to_plain

Utility function to extract the concatenated plain text from a slice of rich text runs.

Rust:

rust
use sheetkit::rich_text_to_plain;

let plain = rich_text_to_plain(&runs);

29. File Encryption

File-level encryption protects the entire .xlsx file with a password. Encrypted files use an OLE/CFB compound container instead of a plain ZIP archive. SheetKit supports:

  • Decryption: Standard Encryption (Office 2007, AES-128-ECB + SHA-1) and Agile Encryption (Office 2010+, AES-256-CBC + SHA-512)
  • Encryption: Agile Encryption (AES-256-CBC + SHA-512, 100,000 iterations)

Requires the encryption feature in Rust: sheetkit = { features = ["encryption"] }. Node.js bindings always include encryption support.

open_with_password(path, password) / openWithPasswordSync(path, password)

Open an encrypted .xlsx file with the given password. Returns an error if the password is incorrect or the file uses an unsupported encryption method.

Rust:

rust
let wb = Workbook::open_with_password("encrypted.xlsx", "secret")?;

TypeScript:

typescript
// Sync
const wb = Workbook.openWithPasswordSync("encrypted.xlsx", "secret");

// Async
const wb2 = await Workbook.openWithPassword("encrypted.xlsx", "secret");

save_with_password(path, password) / saveWithPassword(path, password)

Save the workbook as an encrypted .xlsx file using Agile Encryption.

Rust:

rust
wb.save_with_password("encrypted.xlsx", "secret")?;

TypeScript:

typescript
// Sync
wb.saveWithPassword("encrypted.xlsx", "secret");

// Async
await wb.saveWithPassword("encrypted.xlsx", "secret");

Error Types

ErrorRustTypeScriptDescription
File is encryptedError::FileEncryptedError message: "file is encrypted, password required"Returned by open() when the file is encrypted
Wrong passwordError::IncorrectPasswordError message: "incorrect password"Returned by open_with_password() with wrong password
Unsupported methodError::UnsupportedEncryption(String)Error message: "unsupported encryption method: ..."The encryption version is not supported

Detecting Encrypted Files

When open() encounters an encrypted file, it returns Error::FileEncrypted instead of attempting to parse it. Use open_with_password() to open these files.

Rust:

rust
match Workbook::open("file.xlsx") {
    Ok(wb) => { /* unencrypted file */ }
    Err(sheetkit::Error::FileEncrypted) => {
        let wb = Workbook::open_with_password("file.xlsx", "password")?;
    }
    Err(e) => return Err(e),
}

TypeScript:

typescript
try {
  const wb = Workbook.openSync("file.xlsx");
} catch (e) {
  if (e instanceof Error && e.message.includes("encrypted")) {
    const wb = Workbook.openWithPasswordSync("file.xlsx", "password");
  }
}

Encryption Details

PropertyValue
AlgorithmAES-256-CBC
HashSHA-512
Key derivation iterations100,000
Segment size4,096 bytes
Data integrityHMAC-SHA512
Container formatOLE/CFB (Compound File Binary)

30. Bulk Data Transfer

SheetKit provides three approaches for reading sheet data in the Node.js bindings, each with different memory and performance characteristics. All three use the same underlying binary buffer protocol internally.

getRows(sheet) (TypeScript only)

Returns cell data in the original JsRowData[] format. The binary buffer is decoded transparently, so the return type is backward compatible with earlier versions. Each row contains an array of cell objects with column name, type, and value.

typescript
const rows = wb.getRows('Sheet1');
for (const row of rows) {
  for (const cell of row.cells) {
    console.log(`${cell.column}: ${cell.value ?? cell.numberValue ?? cell.boolValue}`);
  }
}

This is the simplest API and requires no changes to existing code. The buffer transfer eliminates the FFI overhead of the old per-cell napi object creation, but the decoder still creates JS objects for every cell.

getRowsBuffer(sheet) (TypeScript only)

Returns the raw binary Buffer produced by Rust. This is the lowest-level API and is useful when you want to pass the data to a custom decoder, send it over the network, or use it with the SheetData class for deferred access.

typescript
const buf: Buffer = wb.getRowsBuffer('Sheet1');

The buffer follows the SKRD binary format described in the Architecture documentation.

SheetData class (TypeScript only)

The SheetData class wraps a raw buffer and provides O(1) random-access methods for reading individual cells or rows without decoding the entire sheet. Import it from @sheetkit/node/sheet-data.

typescript
import { SheetData } from '@sheetkit/node/sheet-data';

const buf = wb.getRowsBuffer('Sheet1');
const sheet = new SheetData(buf);

Properties:

PropertyTypeDescription
rowCountnumberNumber of rows in the buffer
colCountnumberNumber of columns (bounding rectangle width)

Methods:

getCell(row, col)

Returns the decoded value for a single cell, or null if empty. Row and column are 1-based. The return type depends on the cell type: number for numeric and date cells, string for string/error/formula cells, boolean for boolean cells.

typescript
const value = sheet.getCell(1, 1);   // row 1, column 1 (A1)
const price = sheet.getCell(5, 3);   // row 5, column 3 (C5)

getCellType(row, col)

Returns the type name of a cell as a string: 'empty', 'number', 'string', 'boolean', 'date', 'error', 'formula', or 'string' (for rich text).

typescript
const type = sheet.getCellType(1, 1);  // 'string'
if (type === 'number') {
  const val = sheet.getCell(1, 1);
}

getRow(rowNum)

Returns an array of decoded values for a single row. Empty cells are null. The row number is 1-based.

typescript
const row = sheet.getRow(1);  // [value, value, null, value, ...]

toArray()

Decodes all rows and returns a 2D array. Each element is [] for empty rows, or an array of values with null for empty cells.

typescript
const data = sheet.toArray();
for (const row of data) {
  console.log(row);
}

rows() (generator)

Yields { row: number, values: Array } objects for each row, including empty rows. Useful for streaming-style iteration.

typescript
for (const { row, values } of sheet.rows()) {
  console.log(`Row ${row}:`, values);
}

columnName(colIndex)

Converts a 0-based column index (relative to the buffer's bounding rectangle) to an Excel column name.

typescript
sheet.columnName(0);   // 'A' (if data starts at column A)
sheet.columnName(25);  // 'Z'

When to use each API

APIMemoryLatencyBest for
getRows()Moderate (all cells decoded to objects)All upfrontBackward compatibility, iterating all cells
getRowsBuffer() + SheetDataLow (buffer + on-demand decode)Per-accessLarge sheets, random access, reading a subset of cells
getRowsBuffer() (raw)Minimal (buffer only)NoneCustom decoders, network transfer, caching

31. Sheet View Options

Sheet view options control how a worksheet is displayed in the Excel UI, including gridlines, formula display, zoom level, view mode, and the scroll position.

set_sheet_view_options(sheet, options) / setSheetViewOptions(sheet, options)

Set display options for a sheet. Only non-None/non-undefined fields are applied; other settings are preserved.

Rust:

rust
use sheetkit::sheet::{SheetViewOptions, ViewMode};

wb.set_sheet_view_options("Sheet1", &SheetViewOptions {
    show_gridlines: Some(false),
    show_formulas: Some(true),
    zoom_scale: Some(150),
    view_mode: Some(ViewMode::PageBreak),
    top_left_cell: Some("C10".to_string()),
    ..Default::default()
})?;

TypeScript:

typescript
wb.setSheetViewOptions("Sheet1", {
    showGridlines: false,
    showFormulas: true,
    zoomScale: 150,
    viewMode: "pageBreak",
    topLeftCell: "C10",
});

get_sheet_view_options(sheet) / getSheetViewOptions(sheet)

Get the current sheet view display options.

Rust:

rust
let opts = wb.get_sheet_view_options("Sheet1")?;
println!("Zoom: {:?}", opts.zoom_scale);

TypeScript:

typescript
const opts = wb.getSheetViewOptions("Sheet1");
console.log("Zoom:", opts.zoomScale);

SheetViewOptions

FieldRust TypeTS TypeDescription
show_gridlines / showGridlinesOption<bool>boolean?Show gridlines (default: true)
show_formulas / showFormulasOption<bool>boolean?Show formulas instead of results (default: false)
show_row_col_headers / showRowColHeadersOption<bool>boolean?Show row/column headers (default: true)
zoom_scale / zoomScaleOption<u32>number?Zoom percentage, 10-400 (default: 100)
view_mode / viewModeOption<ViewMode>string?View mode
top_left_cell / topLeftCellOption<String>string?Top-left visible cell (e.g. "A1")

ViewMode

RustTypeScriptDescription
ViewMode::Normal"normal"Normal editing view (default)
ViewMode::PageBreak"pageBreak"Page break preview
ViewMode::PageLayout"pageLayout"Page layout view

Note: Zoom values outside the 10-400 range return an error. Setting view options does not affect existing freeze pane settings.


32. Sheet Visibility

Sheet visibility controls whether a sheet tab appears in the Excel UI. There are three visibility states: visible (default), hidden (user can unhide via the UI), and very hidden (can only be unhidden programmatically).

set_sheet_visibility(sheet, visibility) / setSheetVisibility(sheet, visibility)

Set the visibility state of a sheet. At least one sheet must remain visible at all times. Returns an error if hiding this sheet would leave no visible sheets.

Rust:

rust
use sheetkit::sheet::SheetVisibility;

wb.new_sheet("Hidden")?;
wb.set_sheet_visibility("Hidden", SheetVisibility::Hidden)?;

wb.new_sheet("Secret")?;
wb.set_sheet_visibility("Secret", SheetVisibility::VeryHidden)?;

TypeScript:

typescript
wb.newSheet("Hidden");
wb.setSheetVisibility("Hidden", "hidden");

wb.newSheet("Secret");
wb.setSheetVisibility("Secret", "veryHidden");

get_sheet_visibility(sheet) / getSheetVisibility(sheet)

Get the current visibility state of a sheet.

Rust:

rust
let vis = wb.get_sheet_visibility("Hidden")?;
assert_eq!(vis, SheetVisibility::Hidden);

TypeScript:

typescript
const vis = wb.getSheetVisibility("Hidden"); // "hidden"

SheetVisibility

RustTypeScriptDescription
SheetVisibility::Visible"visible"Sheet tab is visible (default)
SheetVisibility::Hidden"hidden"Hidden, user can unhide via UI
SheetVisibility::VeryHidden"veryHidden"Hidden, can only be unhidden via code

Note: The last visible sheet cannot be hidden. If you attempt to hide the only remaining visible sheet, an error is returned.


33. VBA Project Extraction

Read-only access to VBA macros stored in .xlsm files.

get_vba_project() / getVbaProject()

Returns the raw binary content of xl/vbaProject.bin, or None/null for workbooks without a VBA project.

Rust:

rust
let raw: Option<&[u8]> = wb.get_vba_project();

TypeScript:

typescript
const raw: Buffer | null = wb.getVbaProject();

get_vba_modules() / getVbaModules()

Parses the VBA project binary, decompresses module source code, and returns an array of modules. Returns None/null if no VBA project is present. Raises an error if the VBA project is corrupt.

Rust:

rust
use sheetkit::vba::{VbaModule, VbaModuleType};

if let Some(modules) = wb.get_vba_modules()? {
    for m in &modules {
        println!("{}: {:?}", m.name, m.module_type);
        println!("{}", m.source_code);
    }
}

TypeScript:

typescript
const modules = wb.getVbaModules();
if (modules) {
  for (const m of modules) {
    console.log(`${m.name}: ${m.moduleType}`);
    console.log(m.sourceCode);
  }
}

VbaModule / JsVbaModule

FieldRust typeTypeScript typeDescription
nameStringstringModule name
source_code / sourceCodeStringstringDecompressed VBA source
module_type / moduleTypeVbaModuleTypestringOne of: standard, class, form, document, thisWorkbook

34. Threaded Comments

Threaded comments (Excel 2019+) support conversation-style threads with replies, author tracking via a shared person list, and a resolved/done state. They are stored separately from legacy comments as xl/threadedComments/threadedComment{N}.xml parts.

add_threaded_comment / addThreadedComment

Add a threaded comment to a cell. If the author does not exist in the person list, they are added automatically. Returns the generated comment ID.

Rust:

rust
use sheetkit::ThreadedCommentInput;

let comment_id = wb.add_threaded_comment(
    "Sheet1",
    "A1",
    &ThreadedCommentInput {
        author: "Alice".into(),
        text: "Please review this value.".into(),
        parent_id: None,
    },
)?;

// Reply to an existing comment
wb.add_threaded_comment(
    "Sheet1",
    "A1",
    &ThreadedCommentInput {
        author: "Bob".into(),
        text: "Looks correct to me.".into(),
        parent_id: Some(comment_id.clone()),
    },
)?;

TypeScript:

typescript
const commentId = wb.addThreadedComment("Sheet1", "A1", {
    author: "Alice",
    text: "Please review this value.",
});

// Reply to an existing comment
wb.addThreadedComment("Sheet1", "A1", {
    author: "Bob",
    text: "Looks correct to me.",
    parentId: commentId,
});

get_threaded_comments / getThreadedComments

Get all threaded comments for a sheet.

Rust:

rust
let comments = wb.get_threaded_comments("Sheet1")?;
for c in &comments {
    println!("{}: {} (by {})", c.cell_ref, c.text, c.author);
}

TypeScript:

typescript
const comments = wb.getThreadedComments("Sheet1");

get_threaded_comments_by_cell / getThreadedCommentsByCell

Get threaded comments for a specific cell.

Rust:

rust
let comments = wb.get_threaded_comments_by_cell("Sheet1", "A1")?;

TypeScript:

typescript
const comments = wb.getThreadedCommentsByCell("Sheet1", "A1");

delete_threaded_comment / deleteThreadedComment

Delete a threaded comment by its ID. Returns an error if the comment is not found.

Rust:

rust
wb.delete_threaded_comment("Sheet1", &comment_id)?;

TypeScript:

typescript
wb.deleteThreadedComment("Sheet1", commentId);

resolve_threaded_comment / resolveThreadedComment

Set the resolved (done) state of a threaded comment.

Rust:

rust
wb.resolve_threaded_comment("Sheet1", &comment_id, true)?;

TypeScript:

typescript
wb.resolveThreadedComment("Sheet1", commentId, true);

add_person / addPerson

Add a person to the shared person list. If a person with the same display name already exists, returns their existing ID.

Rust:

rust
use sheetkit::PersonInput;

let person_id = wb.add_person(&PersonInput {
    display_name: "Alice".into(),
    user_id: Some("alice@example.com".into()),
    provider_id: Some("ADAL".into()),
});

TypeScript:

typescript
const personId = wb.addPerson({
    displayName: "Alice",
    userId: "alice@example.com",
    providerId: "ADAL",
});

get_persons / getPersons

Get all persons in the person list.

Rust:

rust
let persons = wb.get_persons();

TypeScript:

typescript
const persons = wb.getPersons();

ThreadedCommentInput / JsThreadedCommentInput

FieldRust typeTypeScript typeDescription
authorStringstringAuthor display name (auto-added to person list)
textStringstringComment text
parent_id / parentIdOption<String>string?Parent comment ID for replies

ThreadedCommentData / JsThreadedCommentData

FieldRust typeTypeScript typeDescription
idStringstringUnique comment ID
cell_ref / cellRefStringstringCell reference (e.g., "A1")
textStringstringComment text
authorStringstringAuthor display name
person_id / personIdStringstringPerson ID from person list
date_time / dateTimeStringstringISO 8601 timestamp
parent_id / parentIdOption<String>string?Parent comment ID (for replies)
doneboolbooleanResolved/done state

PersonInput / JsPersonInput

FieldRust typeTypeScript typeDescription
display_name / displayNameStringstringPerson display name
user_id / userIdOption<String>string?User identifier (e.g., email)
provider_id / providerIdOption<String>string?Identity provider ID

PersonData / JsPersonData

FieldRust typeTypeScript typeDescription
idStringstringUnique person ID
display_name / displayNameStringstringPerson display name
user_id / userIdOption<String>string?User identifier
provider_id / providerIdOption<String>string?Identity provider ID

35. Error Types

All operations that can fail return Result<T, Error> in Rust. In TypeScript, errors are thrown as JavaScript Error objects with the message from the Rust error.

Error Enum Reference

Cell and Reference Errors

VariantMessageDescription
InvalidCellReference(String)invalid cell reference: {0}Not a valid A1-style reference
InvalidRowNumber(u32)invalid row number: {0}Row outside 1..=1,048,576
InvalidColumnNumber(u32)invalid column number: {0}Column outside 1..=16,384
InvalidReference { reference }invalid reference: {reference}Invalid cell range (sqref)
InvalidMergeCellReference(String)invalid merge cell reference: {0}Malformed merge range
CellValueTooLong { length, max }cell value too long: {length} characters (max {max})Value exceeds 32,767 character limit

Sheet Errors

VariantMessageDescription
SheetNotFound { name }sheet '{name}' does not existNamed sheet not in workbook
SheetAlreadyExists { name }sheet '{name}' already existsDuplicate sheet name
InvalidSheetName(String)invalid sheet name: {0}Name violates Excel rules

Style Errors

VariantMessageDescription
StyleNotFound { id }style not found: {id}Style ID not in stylesheet
CellStylesExceeded { max }cell styles exceeded maximum ({max})Too many styles registered
ColumnWidthExceeded { width, max }column width {width} exceeds maximum {max}Width > 255
RowHeightExceeded { height, max }row height {height} exceeds maximum {max}Height > 409
OutlineLevelExceeded { level, max }outline level {level} exceeds maximum {max}Outline level > 7

Merge Cell Errors

VariantMessageDescription
MergeCellOverlap { new, existing }merge cell range '{new}' overlaps with existing range '{existing}'Overlapping merge ranges
MergeCellNotFound(String)merge cell range '{0}' not foundMerge range does not exist

Formula Errors

VariantMessageDescription
CircularReference { cell }circular reference detected at {cell}Dependency cycle in formulas
UnknownFunction { name }unknown function: {name}Unrecognized function name
WrongArgCount { name, expected, got }function {name} expects {expected} arguments, got {got}Incorrect argument count
FormulaError(String)formula evaluation error: {0}General evaluation failure

Named Range Errors

VariantMessageDescription
InvalidDefinedName(String)invalid defined name: {0}Name contains forbidden characters
DefinedNameNotFound { name }defined name '{name}' not foundNamed range does not exist

Feature-Specific Errors

VariantMessageDescription
PivotTableNotFound { name }pivot table '{name}' not foundPivot table does not exist
PivotTableAlreadyExists { name }pivot table '{name}' already existsDuplicate pivot table name
TableNotFound { name }table '{name}' not foundTable does not exist
TableAlreadyExists { name }table '{name}' already existsDuplicate table name
TableColumnNotFound { table, column }column '{column}' not found in table '{table}'Column not in table
InvalidSourceRange(String)invalid source range: {0}Pivot table source range invalid
SlicerNotFound { name }slicer '{name}' not foundSlicer does not exist
SlicerAlreadyExists { name }slicer '{name}' already existsDuplicate slicer name
ThreadedCommentNotFound { id }threaded comment '{id}' not foundComment ID does not exist
ChartNotFound { sheet, cell }no chart found at cell '{cell}' on sheet '{sheet}'No chart at position
PictureNotFound { sheet, cell }no picture found at cell '{cell}' on sheet '{sheet}'No picture at position
UnsupportedImageFormat { format }unsupported image format: {format}Image format not supported

Stream Writer Errors

VariantMessageDescription
StreamRowAlreadyWritten { row }row {row} has already been writtenRows must be in ascending order
StreamAlreadyFinishedstream writer already finishedWriter was already finalized
StreamColumnsAfterRowscannot set column width after rows have been writtenColumn settings must precede rows

File I/O and ZIP Errors

VariantMessageDescription
Io(std::io::Error)I/O error: {0}Underlying OS I/O error
Zip(String)ZIP error: {0}ZIP archive read/write error
XmlParse(String)XML parse error: {0}Malformed XML
XmlDeserialize(String)XML deserialization error: {0}XML does not match expected schema
UnsupportedFileExtension(String)unsupported file extension: {0}Not .xlsx/.xlsm/.xltx/.xltm/.xlam
ZipSizeExceeded { size, limit }ZIP decompressed size {size} bytes exceeds limit of {limit} bytesDecompressed size safety limit
ZipEntryCountExceeded { count, limit }ZIP entry count {count} exceeds limit of {limit}Entry count safety limit

Encryption Errors

VariantMessageDescription
FileEncryptedfile is encrypted, password requiredFile needs password
IncorrectPasswordincorrect passwordWrong decryption password
UnsupportedEncryption(String)unsupported encryption method: {0}Unknown encryption algorithm

Other

VariantMessageDescription
InvalidArgument(String)invalid argument: {0}General invalid parameter
Internal(String)internal error: {0}Unclassified internal error

TypeScript Error Handling

In TypeScript, all errors are thrown as standard Error objects. Match errors by their message string:

typescript
try {
    wb.getCellValue("NonExistent", "A1");
} catch (e) {
    if (e instanceof Error && e.message.includes("does not exist")) {
        console.log("Sheet not found");
    }
}

Released under the MIT / Apache-2.0 License.