Packages

A fast Elixir library for writing Excel (.xlsx) files using Rust. Built with the rust_xlsxwriter crate via Rustler NIF for high performance spreadsheet generation.

Current section

Files

Jump to
xlsx_writer lib xlsx_writer.ex
Raw

lib/xlsx_writer.ex

defmodule XlsxWriter do
@moduledoc """
A high-performance library for creating Excel xlsx files in Elixir.
Built with the powerful `rust_xlsxwriter` crate via Rustler NIF for excellent speed
and memory efficiency. Supports rich formatting, formulas, images, and advanced layout features.
## Quick Start
sheet = XlsxWriter.new_sheet("My Sheet")
|> XlsxWriter.write(0, 0, "Hello", format: [:bold])
|> XlsxWriter.write(0, 1, "World")
{:ok, xlsx_content} = XlsxWriter.generate([sheet])
File.write!("output.xlsx", xlsx_content)
## Key Features
- **Data Types**: Strings, numbers, dates, booleans, URLs, formulas, images
- **Rich Formatting**: Fonts, colors, borders, alignment, number formats
- **Layout Control**: Freeze panes, merged cells, autofilters, hide rows/columns
- **High Performance**: Rust-powered NIF for fast generation of large spreadsheets
## Guides
- [Getting Started](guides/getting_started.md) - Basic usage and data types
- [Builder API](guides/builder_api.md) - High-level API for quick data export (⚠️ Experimental)
- [Advanced Formatting](guides/formatting.md) - Fonts, colors, borders, and number formats
- [Layout Features](guides/layout_features.md) - Freeze panes, merged cells, autofilters, and more
## API Overview
### Core Functions
- `generate/1` - Generate XLSX binary from sheets
- `new_sheet/1` - Create a new worksheet
### Writing Data
- `write/5` - Write any value to a cell
- `write_formula/4` - Write Excel formula
- `write_boolean/5` - Write boolean value
- `write_url/5` - Write clickable URL
- `write_image/4` - Embed image
- `write_comment/5` - Add comment/note to cell
- `write_blank/4` - Write formatted blank cell
### Layout & Structure
- `set_column_width/3`, `set_row_height/3` - Size columns and rows
- `set_column_range_width/4`, `set_row_range_height/4` - Size multiple columns/rows at once
- `freeze_panes/3` - Lock rows/columns when scrolling
- `merge_range/7` - Combine multiple cells
- `hide_row/2`, `hide_column/2` - Hide rows/columns
- `set_autofilter/5` - Add dropdown filters to headers
See the [full documentation](https://hexdocs.pm/xlsx_writer) for detailed function references.
"""
alias XlsxWriter.RustXlsxWriter
alias XlsxWriter.Validation
@doc """
Generates an Excel xlsx file from a list of sheets.
Takes a list of sheet tuples where each tuple contains a sheet name and
a list of instructions for that sheet.
## Parameters
- `sheets` - A list of `{sheet_name, instructions}` tuples
- `opts` - Optional keyword list:
- `:properties` - A `%XlsxWriter.WorkbookProperties{}` struct with document metadata
## Returns
- `{:ok, xlsx_binary}` on success
- `{:error, reason}` on failure
## Examples
iex> sheet = XlsxWriter.new_sheet("Test")
...> |> XlsxWriter.write(0, 0, "Hello")
iex> {:ok, xlsx_content} = XlsxWriter.generate([sheet])
iex> is_binary(xlsx_content)
true
# With document properties
props = %XlsxWriter.WorkbookProperties{author: "John", title: "Report"}
{:ok, content} = XlsxWriter.generate([sheet], properties: props)
"""
def generate(sheets, opts \\ []) when is_list(sheets) do
# It might not be important to reverse the instructions here
# but doing it to avoid potential confusion.
sheets =
Enum.map(sheets, fn {name, instructions} ->
{name, Enum.reverse(instructions)}
end)
result =
case Keyword.get(opts, :properties) do
nil ->
RustXlsxWriter.write(sheets)
%XlsxWriter.WorkbookProperties{} = properties ->
RustXlsxWriter.write_with_properties(sheets, properties)
end
case result do
{:ok, content} ->
{:ok, IO.iodata_to_binary(content)}
other ->
other
end
end
@doc """
Creates a new empty sheet with the given name.
## Parameters
- `name` - The name of the sheet (must be a string)
## Returns
A sheet tuple `{name, []}` ready for writing data.
## Examples
iex> XlsxWriter.new_sheet("My Sheet")
{"My Sheet", []}
"""
def new_sheet(name) when is_binary(name), do: {name, []}
@doc """
Writes a value to a specific cell in the sheet.
Supports various data types including strings, numbers, dates, and Decimal values.
Can also apply formatting options to the cell.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
- `row` - The row index (0-based)
- `col` - The column index (0-based)
- `val` - The value to write
- `opts` - Optional keyword list with formatting options
## Formatting Options
- `:format` - A list of format specifications:
- `:bold` - Make text bold
- `:italic` - Make text italic
- `:strikethrough` - Strike through text
- `:superscript` - Superscript text
- `:subscript` - Subscript text
- `:text_wrap` - Wrap text within the cell
- `{:rotation, angle}` - Rotate text (-90 to 90 degrees, or 270 for vertical stacked text)
- `:shrink` - Shrink text to fit within the cell width
- `{:indent, level}` - Indent text by the given level (integer)
- `{:align, :left | :center | :right}` - Horizontal text alignment
- `{:valign, :top | :center | :bottom | :justify | :distributed}` - Vertical text alignment
- `{:num_format, format_string}` - Custom number format
- `{:bg_color, hex_color}` - Background color (e.g., "#FFFF00" for yellow)
- `{:font_color, hex_color}` - Font color (e.g., "#FF0000" for red)
- `{:font_size, size}` - Font size in points (e.g., 12, 14, 16)
- `{:font_name, name}` - Font family (e.g., "Arial", "Times New Roman")
- `{:underline, :single | :double | :single_accounting | :double_accounting}` - Underline style
- `{:pattern, :solid | :none | :gray125 | :gray0625}` - Fill pattern
- `{:border, style}` - Apply border to all sides (see border styles below)
- `{:border_top, style}` - Top border
- `{:border_bottom, style}` - Bottom border
- `{:border_left, style}` - Left border
- `{:border_right, style}` - Right border
- `{:border_color, hex_color}` - Color for all borders
- `{:border_top_color, hex_color}` - Top border color
- `{:border_bottom_color, hex_color}` - Bottom border color
- `{:border_left_color, hex_color}` - Left border color
- `{:border_right_color, hex_color}` - Right border color
## Border Styles
Available border styles: `:thin`, `:medium`, `:thick`, `:dashed`, `:dotted`, `:double`,
`:hair`, `:medium_dashed`, `:dash_dot`, `:medium_dash_dot`, `:dash_dot_dot`,
`:medium_dash_dot_dot`, `:slant_dash_dot`
## Returns
Updated sheet tuple with the new write instruction.
## Examples
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write(sheet, 0, 0, "Hello")
iex> {"Test", [{:write, 0, 0, {:string, "Hello"}}]} = sheet
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write(sheet, 0, 0, "Bold", format: [:bold])
iex> {"Test", [{:write, 0, 0, {:string_with_format, "Bold", [:bold]}}]} = sheet
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write(sheet, 0, 0, "Yellow", format: [{:bg_color, "#FFFF00"}])
iex> {"Test", [{:write, 0, 0, {:string_with_format, "Yellow", [{:bg_color, "#FFFF00"}]}}]} = sheet
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write(sheet, 0, 0, "Red Italic", format: [:italic, {:font_color, "#FF0000"}])
iex> {"Test", [{:write, 0, 0, {:string_with_format, "Red Italic", [:italic, {:font_color, "#FF0000"}]}}]} = sheet
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write(sheet, 0, 0, "Bordered", format: [{:border, :thin}])
iex> {"Test", [{:write, 0, 0, {:string_with_format, "Bordered", [{:border, :thin}]}}]} = sheet
"""
def write({name, instructions}, row, col, val, opts \\ []) do
Validation.validate_cell_position!(row, col)
case Keyword.get(opts, :format) do
nil ->
{name, [{:write, row, col, to_rust_val(val)} | instructions]}
formats when is_list(formats) ->
write_with_format({name, instructions}, row, col, val, formats)
end
end
@doc """
Writes an Excel formula to a specific cell in the sheet.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
- `row` - The row index (0-based)
- `col` - The column index (0-based)
- `val` - The Excel formula string (should start with '=')
- `opts` - Optional keyword list with formatting options
## Options
- `:format` - A list of format specifications
## Returns
Updated sheet tuple with the new formula instruction.
## Examples
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write_formula(sheet, 0, 2, "=A1+B1")
iex> {"Test", [{:write, 0, 2, {:formula, "=A1+B1"}}]} = sheet
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write_formula(sheet, 0, 2, "=A1+B1", format: [:bold])
iex> {"Test", [{:write, 0, 2, {:formula_with_format, "=A1+B1", [:bold]}}]} = sheet
"""
def write_formula({name, instructions}, row, col, val, opts \\ []) do
Validation.validate_cell_position!(row, col)
case Keyword.get(opts, :format) do
nil ->
{name, [{:write, row, col, {:formula, val}} | instructions]}
formats when is_list(formats) ->
Validation.validate_formats!(formats)
{name, [{:write, row, col, {:formula_with_format, val, formats}} | instructions]}
end
end
@doc """
Writes a boolean value to a specific cell in the sheet.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
- `row` - The row index (0-based)
- `col` - The column index (0-based)
- `val` - The boolean value (true or false)
- `opts` - Optional keyword list with formatting options
## Returns
Updated sheet tuple with the new boolean instruction.
## Examples
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write_boolean(sheet, 0, 0, true)
iex> {"Test", [{:write, 0, 0, {:boolean, true}}]} = sheet
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write_boolean(sheet, 0, 0, false, format: [:bold])
iex> {"Test", [{:write, 0, 0, {:boolean_with_format, false, [:bold]}}]} = sheet
"""
def write_boolean({name, instructions}, row, col, val, opts \\ [])
when is_boolean(val) do
Validation.validate_cell_position!(row, col)
case Keyword.get(opts, :format) do
nil ->
{name, [{:write, row, col, {:boolean, val}} | instructions]}
formats when is_list(formats) ->
Validation.validate_formats!(formats)
{name,
[
{:write, row, col, {:boolean_with_format, val, formats}}
| instructions
]}
end
end
@doc """
Writes a URL/hyperlink to a specific cell in the sheet.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
- `row` - The row index (0-based)
- `col` - The column index (0-based)
- `url` - The URL string
- `opts` - Optional keyword list with:
- `:text` - Display text (different from URL)
- `:format` - Format specifications
## Returns
Updated sheet tuple with the new URL instruction.
## Examples
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write_url(sheet, 0, 0, "https://example.com")
iex> {"Test", [{:write, 0, 0, {:url, "https://example.com"}}]} = sheet
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write_url(sheet, 0, 0, "https://example.com", text: "Click here")
iex> {"Test", [{:write, 0, 0, {:url_with_text, "https://example.com", "Click here"}}]} = sheet
"""
def write_url({name, instructions}, row, col, url, opts \\ [])
when is_binary(url) do
Validation.validate_cell_position!(row, col)
text = Keyword.get(opts, :text)
formats = Keyword.get(opts, :format)
if formats && is_list(formats), do: Validation.validate_formats!(formats)
instruction =
case {text, formats} do
{nil, nil} ->
{:write, row, col, {:url, url}}
{text, nil} when is_binary(text) ->
{:write, row, col, {:url_with_text, url, text}}
{nil, formats} when is_list(formats) ->
{:write, row, col, {:url_with_format, url, formats}}
{text, formats} when is_binary(text) and is_list(formats) ->
{:write, row, col, {:url_with_text_and_format, url, text, formats}}
end
{name, [instruction | instructions]}
end
@doc """
Writes a blank cell with formatting to the sheet.
A blank cell differs from an empty cell - it has no data but can have formatting.
This is useful for pre-formatting cells before data is added.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
- `row` - The row index (0-based)
- `col` - The column index (0-based)
- `opts` - Keyword list with `:format` specifications
## Returns
Updated sheet tuple with the new blank cell instruction.
## Examples
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write_blank(sheet, 0, 0, format: [:bold, {:bg_color, "#FFFF00"}])
iex> {"Test", [{:write, 0, 0, {:blank, [:bold, {:bg_color, "#FFFF00"}]}}]} = sheet
"""
def write_blank({name, instructions}, row, col, opts \\ []) do
Validation.validate_cell_position!(row, col)
formats = Keyword.get(opts, :format, [])
if is_list(formats) && formats != [],
do: Validation.validate_formats!(formats)
{name, [{:write, row, col, {:blank, formats}} | instructions]}
end
defp write_with_format(sheet, row, col, nil, formats) do
write_with_format(sheet, row, col, "", formats)
end
defp write_with_format({name, instructions}, row, col, val, formats)
when is_binary(val) do
Validation.validate_formats!(formats)
instruction = {:write, row, col, {:string_with_format, val, formats}}
{name, [instruction | instructions]}
end
defp write_with_format({name, instructions}, row, col, numeric_val, formats)
when is_number(numeric_val) do
Validation.validate_formats!(formats)
instruction =
{:write, row, col, {:number_with_format, numeric_val, formats}}
{name, [instruction | instructions]}
end
@doc """
Writes a rich text string to a specific cell in the sheet.
A rich string allows different formatting for different parts of the text
within a single cell. Each segment consists of text and optional formatting.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
- `row` - The row index (0-based)
- `col` - The column index (0-based)
- `segments` - A list of `{text, formats}` tuples, where:
- `text` is a string
- `formats` is a list of format options (can be empty `[]` for default formatting)
- `opts` - Optional keyword list with:
- `:format` - Cell-level formatting (alignment, borders, background, etc.)
## Segment Format Options
Each segment can have text formatting options:
- `:bold` - Make text bold
- `:italic` - Make text italic
- `:strikethrough` - Strike through text
- `:superscript` - Superscript text
- `:subscript` - Subscript text
- `{:font_color, hex_color}` - Font color (e.g., "#FF0000" for red)
- `{:font_size, size}` - Font size in points
- `{:font_name, name}` - Font family
- `{:underline, style}` - Underline style
## Returns
Updated sheet tuple with the new rich string instruction.
## Examples
# Simple bold and normal text
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write_rich_string(sheet, 0, 0, [
...> {"Bold ", [:bold]},
...> {"Normal", []}
...> ])
iex> {"Test", [{:write, 0, 0, {:rich_string, [{"Bold ", [:bold]}, {"Normal", []}]}}]} = sheet
# Colored text segments
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write_rich_string(sheet, 0, 0, [
...> {"Red ", [{:font_color, "#FF0000"}]},
...> {"Blue", [{:font_color, "#0000FF"}]}
...> ])
iex> {"Test", [{:write, 0, 0, {:rich_string, [{"Red ", [{:font_color, "#FF0000"}]}, {"Blue", [{:font_color, "#0000FF"}]}]}}]} = sheet
# With cell-level formatting (centered)
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write_rich_string(sheet, 0, 0, [
...> {"Bold ", [:bold]},
...> {"Italic", [:italic]}
...> ], format: [{:align, :center}])
iex> {"Test", [{:write, 0, 0, {:rich_string_with_format, [{"Bold ", [:bold]}, {"Italic", [:italic]}], [{:align, :center}]}}]} = sheet
"""
def write_rich_string({name, instructions}, row, col, segments, opts \\ []) do
Validation.validate_cell_position!(row, col)
Validation.validate_rich_string_segments!(segments)
case Keyword.get(opts, :format) do
nil ->
{name, [{:write, row, col, {:rich_string, segments}} | instructions]}
formats when is_list(formats) ->
Validation.validate_formats!(formats)
{name,
[
{:write, row, col, {:rich_string_with_format, segments, formats}}
| instructions
]}
end
end
@doc """
Writes an image to a specific cell in the sheet.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
- `row` - The row index (0-based)
- `col` - The column index (0-based)
- `image_binary` - The binary content of the image file
## Returns
Updated sheet tuple with the new image instruction.
## Examples
iex> image_data = <<137, 80, 78, 71>> # Mock PNG header
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write_image(sheet, 0, 0, image_data)
iex> {"Test", [{:write, 0, 0, {:image, ^image_data}}]} = sheet
"""
def write_image({name, instructions}, row, col, image_binary) do
Validation.validate_cell_position!(row, col)
Validation.validate_image_binary!(image_binary)
{name, [{:write, row, col, {:image, image_binary}} | instructions]}
end
@doc """
Writes a comment/note to a specific cell in the sheet.
Comments appear when hovering over a cell and are useful for documentation,
instructions, or additional context about cell values.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
- `row` - The row index (0-based)
- `col` - The column index (0-based)
- `text` - The comment text content
- `opts` - Optional keyword list:
- `:author` - Author name (string, max 52 characters)
- `:visible` - Whether to show the comment by default (boolean, default: false)
- `:width` - Comment box width in pixels (integer, default: 128)
- `:height` - Comment box height in pixels (integer, default: 74)
## Returns
Updated sheet tuple with the new comment instruction.
## Examples
# Simple comment
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write_comment(sheet, 0, 0, "This is a note")
iex> {"Test", [{:insert_note, 0, 0, "This is a note", _}]} = sheet
# Comment with author
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write_comment(sheet, 0, 0, "Review this", author: "John Doe")
iex> {"Test", [{:insert_note, 0, 0, "Review this", %XlsxWriter.NoteOptions{author: "John Doe"}}]} = sheet
# Visible comment with custom size
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.write_comment(sheet, 0, 0, "Important!",
...> visible: true, width: 300, height: 200)
iex> {"Test", [{:insert_note, 0, 0, "Important!", options}]} = sheet
iex> options.visible
true
iex> options.width
300
"""
def write_comment({name, instructions}, row, col, text, opts \\ []) do
Validation.validate_cell_position!(row, col)
if !is_binary(text) do
raise ArgumentError,
"Comment text must be a string, got: #{inspect(text)}"
end
note_options = %XlsxWriter.NoteOptions{
author: Keyword.get(opts, :author),
visible: Keyword.get(opts, :visible),
width: Keyword.get(opts, :width),
height: Keyword.get(opts, :height)
}
{name, [{:insert_note, row, col, text, note_options} | instructions]}
end
@doc """
Sets the width of a specific column in the sheet.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
- `col` - The column index (0-based)
- `width` - The width value (typically a float)
## Returns
Updated sheet tuple with the new column width instruction.
## Examples
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.set_column_width(sheet, 0, 25)
iex> {"Test", [{:set_column_width, 0, 25}]} = sheet
"""
def set_column_width({name, instructions}, col, width) do
{name, [{:set_column_width, col, width} | instructions]}
end
@doc """
Sets the height of a specific row in the sheet.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
- `row` - The row index (0-based)
- `height` - The height value (typically a float)
## Returns
Updated sheet tuple with the new row height instruction.
## Examples
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.set_row_height(sheet, 0, 30.0)
iex> {"Test", [{:set_row_height, 0, 30.0}]} = sheet
"""
def set_row_height({name, instructions}, row, height) do
{name, [{:set_row_height, row, height} | instructions]}
end
@doc """
Sets the width for a range of columns in the sheet.
This is a convenience function to set the same width for multiple consecutive columns.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
- `first_col` - The first column index (0-based)
- `last_col` - The last column index (0-based, inclusive)
- `width` - The width value in pixels
## Returns
Updated sheet tuple with the new column range width instruction.
## Examples
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.set_column_range_width(sheet, 0, 4, 20)
iex> {"Test", [{:set_column_range_width, 0, 4, 20}]} = sheet
"""
def set_column_range_width({name, instructions}, first_col, last_col, width) do
{name,
[{:set_column_range_width, first_col, last_col, width} | instructions]}
end
@doc """
Sets the height for a range of rows in the sheet.
This is a convenience function to set the same height for multiple consecutive rows.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
- `first_row` - The first row index (0-based)
- `last_row` - The last row index (0-based, inclusive)
- `height` - The height value in pixels
## Returns
Updated sheet tuple with the new row range height instruction.
## Examples
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.set_row_range_height(sheet, 0, 9, 25)
iex> {"Test", [{:set_row_range_height, 0, 9, 25}]} = sheet
"""
def set_row_range_height({name, instructions}, first_row, last_row, height) do
{name,
[{:set_row_range_height, first_row, last_row, height} | instructions]}
end
@doc """
Freezes panes at the specified row and column.
This locks rows and/or columns so they remain visible when scrolling.
Very useful for keeping headers visible.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
- `row` - The row to freeze at (0-based). Rows above this remain visible.
- `col` - The column to freeze at (0-based). Columns left of this remain visible.
## Returns
Updated sheet tuple with the freeze panes instruction.
## Examples
# Freeze the first row (header row)
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.freeze_panes(sheet, 1, 0)
iex> {"Test", [{:set_freeze_panes, 1, 0}]} = sheet
# Freeze first column
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.freeze_panes(sheet, 0, 1)
iex> {"Test", [{:set_freeze_panes, 0, 1}]} = sheet
# Freeze first row and first column
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.freeze_panes(sheet, 1, 1)
iex> {"Test", [{:set_freeze_panes, 1, 1}]} = sheet
"""
def freeze_panes({name, instructions}, row, col) do
{name, [{:set_freeze_panes, row, col} | instructions]}
end
@doc """
Hides a specific row in the sheet.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
- `row` - The row index to hide (0-based)
## Returns
Updated sheet tuple with the hide row instruction.
## Examples
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.hide_row(sheet, 5)
iex> {"Test", [{:set_row_hidden, 5}]} = sheet
"""
def hide_row({name, instructions}, row) do
{name, [{:set_row_hidden, row} | instructions]}
end
@doc """
Hides a specific column in the sheet.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
- `col` - The column index to hide (0-based)
## Returns
Updated sheet tuple with the hide column instruction.
## Examples
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.hide_column(sheet, 2)
iex> {"Test", [{:set_column_hidden, 2}]} = sheet
"""
def hide_column({name, instructions}, col) do
{name, [{:set_column_hidden, col} | instructions]}
end
@doc """
Sets an autofilter on a range of cells.
Adds dropdown filter buttons to the specified range, typically used on header rows.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
- `first_row` - The first row of the filter range (0-based)
- `first_col` - The first column of the filter range (0-based)
- `last_row` - The last row of the filter range (0-based)
- `last_col` - The last column of the filter range (0-based)
## Returns
Updated sheet tuple with the autofilter instruction.
## Examples
# Set autofilter on header row (row 0, columns A-E)
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.set_autofilter(sheet, 0, 0, 0, 4)
iex> {"Test", [{:set_autofilter, 0, 0, 0, 4}]} = sheet
"""
def set_autofilter(
{name, instructions},
first_row,
first_col,
last_row,
last_col
) do
{name,
[
{:set_autofilter, first_row, first_col, last_row, last_col}
| instructions
]}
end
@doc """
Enables autofit for all columns in the sheet.
Automatically adjusts column widths to fit the longest content in each column.
This is applied after all data is written. Note that explicit column widths
set via `set_column_width/3` will take precedence.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
## Returns
Updated sheet tuple with the autofit instruction.
## Examples
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.autofit(sheet)
iex> {"Test", [:set_autofit]} = sheet
"""
def autofit({name, instructions}) do
{name, [:set_autofit | instructions]}
end
@doc """
Sets the color of the worksheet tab.
This changes the color of the sheet tab at the bottom of the Excel window,
useful for visually organizing multi-sheet workbooks.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
- `color` - Hex color string (e.g., "#FF0000" for red)
## Returns
Updated sheet tuple with the tab color instruction.
## Examples
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.set_tab_color(sheet, "#FF0000")
iex> {"Test", [{:set_tab_color, "#FF0000"}]} = sheet
"""
def set_tab_color({name, instructions}, color) when is_binary(color) do
{name, [{:set_tab_color, color} | instructions]}
end
@doc """
Merges a range of cells into a single cell.
The merged cell will contain the specified value and formatting.
All merged cells will appear as one cell in Excel.
## Parameters
- `sheet` - The sheet tuple `{name, instructions}`
- `first_row` - The first row of the merge range (0-based)
- `first_col` - The first column of the merge range (0-based)
- `last_row` - The last row of the merge range (0-based)
- `last_col` - The last column of the merge range (0-based)
- `val` - The value to write in the merged cell
- `opts` - Optional keyword list with formatting options
## Returns
Updated sheet tuple with the merge range instruction.
## Examples
# Merge cells A1:D1 with centered title
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.merge_range(sheet, 0, 0, 0, 3, "Title", format: [:bold, {:align, :center}])
iex> {"Test", [{:merge_range, 0, 0, 0, 3, {:string_with_format, "Title", [:bold, {:align, :center}]}}]} = sheet
# Merge cells for a number
iex> sheet = XlsxWriter.new_sheet("Test")
iex> sheet = XlsxWriter.merge_range(sheet, 1, 1, 3, 1, 100)
iex> {"Test", [{:merge_range, 1, 1, 3, 1, {:float, 100}}]} = sheet
"""
def merge_range(
{name, instructions},
first_row,
first_col,
last_row,
last_col,
val,
opts \\ []
) do
case Keyword.get(opts, :format) do
nil ->
{name,
[
{:merge_range, first_row, first_col, last_row, last_col,
to_rust_val(val)}
| instructions
]}
formats when is_list(formats) ->
merge_range_with_format(
{name, instructions},
first_row,
first_col,
last_row,
last_col,
val,
formats
)
end
end
defp merge_range_with_format(
{name, instructions},
first_row,
first_col,
last_row,
last_col,
val,
formats
)
when is_binary(val) do
Validation.validate_formats!(formats)
instruction =
{:merge_range, first_row, first_col, last_row, last_col,
{:string_with_format, val, formats}}
{name, [instruction | instructions]}
end
defp merge_range_with_format(
{name, instructions},
first_row,
first_col,
last_row,
last_col,
numeric_val,
formats
)
when is_number(numeric_val) do
Validation.validate_formats!(formats)
instruction =
{:merge_range, first_row, first_col, last_row, last_col,
{:number_with_format, numeric_val, formats}}
{name, [instruction | instructions]}
end
defp merge_range_with_format(
{name, instructions},
first_row,
first_col,
last_row,
last_col,
val,
formats
)
when is_boolean(val) do
Validation.validate_formats!(formats)
instruction =
{:merge_range, first_row, first_col, last_row, last_col,
{:boolean_with_format, val, formats}}
{name, [instruction | instructions]}
end
defp to_rust_val(%Decimal{} = amount), do: {:float, Decimal.to_float(amount)}
defp to_rust_val(%Date{} = date), do: {:date, Date.to_iso8601(date)}
defp to_rust_val(%DateTime{} = datetime), do: {:date_time, DateTime.to_iso8601(datetime)}
defp to_rust_val(%NaiveDateTime{} = datetime),
do: {:date_time, NaiveDateTime.to_iso8601(datetime)}
defp to_rust_val(val) when is_binary(val), do: {:string, val}
defp to_rust_val(val) when is_float(val), do: {:float, val}
defp to_rust_val(val) when is_integer(val), do: {:float, val}
defp to_rust_val(nil), do: {:string, ""}
defp to_rust_val(val) when is_atom(val), do: {:string, Atom.to_string(val)}
defp to_rust_val(other) do
raise XlsxWriter.Error,
"The data type for value \"#{inspect(other)}\" is not supported."
end
end