Formatting¶
Cell appearance is controlled by three groups of options: the header row
(header_format), the data columns (column_formats, matched by name or pattern), and
the sheet geometry (widths, heights, merged ranges). Format dictionaries reject unknown
keys rather than ignoring them, so a typo is an error and not a silently missing
style.
Header Styling¶
Apply custom formatting to header cells:
import xlsxturbo
import pandas as pd
df = pd.DataFrame({'Name': ['Alice', 'Bob'], 'Score': [95, 87]})
# Bold headers
xlsxturbo.df_to_xlsx(df, "bold.xlsx", header_format={'bold': True})
# Full styling with colors
xlsxturbo.df_to_xlsx(df, "styled.xlsx", header_format={
'bold': True,
'bg_color': '#4F81BD', # Blue background
'font_color': 'white' # White text
})
# Available options:
# - bold (bool): Bold text
# - italic (bool): Italic text
# - font_color (str): '#RRGGBB' or named color (white, black, red, blue, etc.)
# - bg_color (str): Background color
# - font_size (float): Font size in points
# - font_name (str): Font family, e.g. 'Arial'
# - quote_prefix (bool): Excel quote marker; leaves the value and type unchanged
# - underline (bool): Underlined text
# - border (bool|str): True = thin all sides, or a style name
# - border_left/right/top/bottom (bool|str): Per-side border, same values
# - border_color (str): Color for all borders
# - align_horizontal (str): 'left', 'center', 'right', 'fill', 'justify',
# 'center_across', 'distributed'
# - align_vertical (str): 'top', 'center', 'bottom', 'justify', 'distributed'
# - wrap_text (bool): Enable text wrapping within cell
Note: Unknown keys (e.g.
'color'instead of'font_color') and wrong value types raise an error listing the valid options. Applies toheader_format,column_formats,conditional_formats[...]['format'],images,validations,textboxes,charts,sparklines, andrich_textsegment formats.
rich_textsegments accept font-level keys only (bold,italic,underline,font_color,font_size,font_name). A segment is an inline run inside one cell, so cell-level keys —bg_color, borders,align_horizontal/align_vertical,wrap_text,quote_prefix— would never render and are rejected rather than silently ignored. Format the cell itself viacolumn_formatsorcellsinstead.Column patterns in
column_formats,conditional_formats, andvalidationsmust match at least one DataFrame column. A zero-match exact name or wildcard raisesValueErrorinstead of silently omitting the requested behavior.
Column Formatting¶
font_name and quote_prefix also work in merged-range formats. They are not
supported in conditional-format rules; set them through column_formats instead.
quote_prefix changes Excel's quote marker, not the stored value or type. Pass
identifiers as strings and use num_format='@' when text formatting is needed.
Apply formatting to data columns using pattern matching. Unknown keys raise errors (see Header Styling).
import xlsxturbo
import pandas as pd
df = pd.DataFrame({
'product_id': [1, 2, 3],
'product_name': ['Widget A', 'Widget B', 'Widget C'],
'price_usd': [19.99, 29.99, 39.99],
'price_eur': [17.99, 26.99, 35.99],
'quantity': [100, 75, 50]
})
# Format columns by pattern
xlsxturbo.df_to_xlsx(df, "report.xlsx", column_formats={
'price_*': {'num_format': '$#,##0.00', 'bg_color': '#E8F5E9'}, # All price columns
'quantity': {'bold': True} # Exact match
})
# Wildcard patterns:
# - 'prefix*' matches columns starting with 'prefix'
# - '*suffix' matches columns ending with 'suffix'
# - '*contains*' matches columns containing 'contains'
# - 'exact' matches column name exactly
# Available format options:
# - bg_color (str): Background color ('#RRGGBB' or named)
# - font_color (str): Text color
# - font_name (str): Font family, e.g. 'Arial'
# - quote_prefix (bool): Excel quote marker; leaves the value and type unchanged
# - num_format (str): Excel number format ('0.00', '#,##0', '0.00%', etc.)
# Date and datetime values keep 'yyyy-mm-dd' / 'yyyy-mm-dd hh:mm:ss' unless this is set.
# - bold (bool): Bold text
# - italic (bool): Italic text
# - underline (bool): Underlined text
# - border (bool|str): True = thin all sides, or a style name for all sides
# - border_left (bool|str): True = thin, or a style name, left side only
# - border_right (bool|str): True = thin, or a style name, right side only
# - border_top (bool|str): True = thin, or a style name, top side only
# - border_bottom (bool|str): True = thin, or a style name, bottom side only
# - border_color (str): Color for all borders ('#RRGGBB' or named)
#
# Every border key takes either a bool or a style name. True means thin;
# False means no border, which is the same as omitting the key.
# 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
# - align_horizontal (str): 'left', 'center', 'right', 'fill', 'justify',
# 'center_across', 'distributed'
# - align_vertical (str): 'top', 'center', 'bottom', 'justify', 'distributed'
# - wrap_text (bool): Enable text wrapping within cell
# First matching pattern wins (order preserved)
xlsxturbo.df_to_xlsx(df, "report.xlsx", column_formats={
'price_usd': {'bg_color': '#FFEB3B'}, # Specific: yellow for USD
'price_*': {'bg_color': '#E3F2FD'} # General: blue for other prices
})
# Per-side borders with style control
xlsxturbo.df_to_xlsx(df, "report.xlsx", column_formats={
'price_usd': {'border_right': 'thick'}, # Thick right border only
'quantity': {'border': 'thin'}, # Thin border all sides
'product_name': {'border_left': 'medium', 'border_right': 'medium'}, # Left+right
})
Custom Column Widths and Row Heights¶
import xlsxturbo
import pandas as pd
df = pd.DataFrame({
'Name': ['Alice', 'Bob', 'Charlie'],
'Department': ['Engineering', 'Marketing', 'Sales'],
'Salary': [75000, 65000, 55000]
})
# Set specific column widths (column index -> width in characters)
xlsxturbo.df_to_xlsx(df, "report.xlsx",
column_widths={0: 20, 1: 25, 2: 15}
)
# Set specific row heights (row index -> height in points)
xlsxturbo.df_to_xlsx(df, "report.xlsx",
row_heights={0: 25} # Make header row taller
)
# Combine with other options
xlsxturbo.df_to_xlsx(df, "styled.xlsx",
table_style="Medium9",
freeze_panes=True,
column_widths={0: 20, 1: 30, 2: 15},
row_heights={0: 22}
)
Excel's own limits¶
Excel caps a column at 255 characters and a row at 409.5 points. A larger value
is accepted rather than rejected, and nothing is lost by asking for one: widths
above the cap are already clamped as the file is written (255, 300 and
1,000,000 all produce the same stored width), and Excel clamps an over-limit
row height when it loads the workbook. Measured on Excel for Mac 16: a workbook
written with row_heights={0: 500} or {0: 1e6} opens with no repair prompt
and shows a height of 409.5, and one written with column_widths={0: 1e6}
shows the same width as {0: 255}. So the values below the cap are the ones
worth writing, but an over-limit one costs only its own precision.
Global Column Width Cap¶
Use column_widths={'_all': value} to cap all columns at a maximum width:
import xlsxturbo
import pandas as pd
df = pd.DataFrame({
'Name': ['Alice', 'Bob'],
'VeryLongDescription': ['A' * 100, 'B' * 100],
'Score': [95, 87]
})
# Cap all columns at 30 characters
xlsxturbo.df_to_xlsx(df, "capped.xlsx", column_widths={'_all': 30})
# Mix specific widths with global cap (specific overrides '_all')
xlsxturbo.df_to_xlsx(df, "mixed.xlsx", column_widths={0: 15, '_all': 30})
# Autofit with cap: fit content, but never exceed 25 characters
xlsxturbo.df_to_xlsx(df, "fitted.xlsx", autofit=True, column_widths={'_all': 25})
When autofit=True is combined with column_widths that names specific columns and has no '_all' key, the named columns get their explicit widths and every other column is autofitted to its content. Add '_all' back in to cap the autofitted columns instead of leaving them uncapped.
Merged Cells¶
Merge cell ranges to create headers, titles, or grouped labels:
import xlsxturbo
import pandas as pd
df = pd.DataFrame({
'product': ['Widget A', 'Widget B'],
'sales': [1500, 2300],
'revenue': [7500, 11500]
})
# Merge cells for a title above the data
xlsxturbo.df_to_xlsx(df, "report.xlsx",
header=True,
merged_ranges=[
# Simple merge with text (auto-centered)
('A1:C1', 'Q4 Sales Report'),
# Merge with custom formatting
('A2:C2', 'Regional Data', {
'bold': True,
'bg_color': '#4F81BD',
'font_color': 'white'
})
]
)
Merged range format:
- Tuple of (range, text) or (range, text, format_dict)
- Range uses Excel notation: 'A1:D1', 'B3:B10', etc.
- Format options are the same set header_format takes -- the font keys (bold, italic,
underline, font_color, font_size, font_name) and the cell keys (bg_color, border and
the four per-side border keys, border_color, align_horizontal, align_vertical, wrap_text,
quote_prefix). Only
num_format is out of scope; that one is accepted by column_formats and the nested
format keys.
Notes:
- Merged cells are applied after data is written, so plan row positions accordingly
- When using with header=True, data starts at row 2 (Excel row 2)
- Works with both df_to_xlsx and dfs_to_xlsx (global or per-sheet)
Rich Text¶
Multiple formats within a single cell:
import xlsxturbo
import pandas as pd
df = pd.DataFrame({'A': [1, 2, 3]})
xlsxturbo.df_to_xlsx(df, "rich.xlsx",
rich_text={
'D1': [
('Important: ', {'bold': True, 'font_color': 'red'}),
'Please review ',
('all', {'italic': True}),
' values'
],
'D2': [
('Status: ', {'bold': True}),
('OK', {'font_color': 'green', 'bold': True})
]
}
)
Segment format:
- Formatted: ('text', {'bold': True, 'font_color': 'blue'})
- Plain: 'plain text' (no formatting)
Available format options:
- bold (bool)
- italic (bool)
- font_color (str): '#RRGGBB' or named color
- bg_color (str): Background color
- font_size (float)
- underline (bool)
Notes:
- Rich text writes to the specified cell position (overwrites existing content)
- Works with both df_to_xlsx and dfs_to_xlsx (global or per-sheet)
- Not available in constant memory mode