Every exported report has one or two columns that refuse to stay in their place: order notes, error logs, product descriptions. By default the text runs sideways, either spilling over the neighbouring columns or forcing the column width to something absurd. Widen only the row and the text still will not wrap — it simply gets clipped. A handful of rows can be dragged by hand; a few hundred files cannot.
Excel offers several complementary ways to deal with long text: wrapping breaks the text inside the cell, shrink-to-fit compresses it to the available width, indentation shifts short text horizontally, and auto-fitting recalculates column widths and row heights from the content. This article demonstrates each of these with Spire.XLS for Python, then shows how to combine them.
Why Handle Cell Layout in a Script
- Consistent across sheets: one wrap-and-fit rule applies to every worksheet, with no column dragging.
- Predictable results: width, height, and indentation are parameters instead of hidden state left by a mouse gesture.
- Composable: wrap then fit, or indent then resize — the order is written down and reproducible.
Setting Up the Environment
Install the Spire.XLS library:
pip install Spire.XLS
Then import the required modules in your script:
from spire.xls import *
from spire.xls.common import *
Wrapping Text
Wrapping is controlled by WrapText on the cell style. Once enabled, text folds onto the next line when it reaches the column boundary, and Excel adjusts the row height to fit:
from spire.xls import *
from spire.xls.common import *
workbook = Workbook()
sheet = workbook.Worksheets[0]
sheet.Range["C1"].Text = "Long descriptions wrap inside the cell when the column is narrow enough"
sheet.Range["C1"].Style.WrapText = True
sheet.Range["C2"].Text = "https://example.com/a/very/long/url/that/should/not/wrap"
sheet.Range["C2"].Style.WrapText = False
workbook.SaveToFile("WrapText.xlsx", ExcelVersion.Version2013)
workbook.Dispose()
Range.Style.WrapText applies to the whole range, so a column can be configured in one call rather than cell by cell. Content that must not break — URLs, identifiers — is better left at False, because a wrapped URL is harder to read than a clipped one.
Controlling Break Points with Line Breaks
Wrapping chooses break points from the column width, but some content should break at a specific place: the lines of an address, or a note written as a short list. Insert a newline character \n in the text and enable wrapping as before:
from spire.xls import *
from spire.xls.common import *
workbook = Workbook()
sheet = workbook.Worksheets[0]
cell = sheet.Range["C5"]
# Set the column width first, then write text with explicit breaks
sheet.SetColumnWidth(cell.Column, 70)
cell.Value = "Line one: order details\nLine two: shipping address\nLine three: remarks"
cell.IsWrapText = True
workbook.SaveToFile("ExplicitLineBreaks.xlsx", ExcelVersion.Version2013)
workbook.Dispose()
The single-cell property here is cell.IsWrapText, which behaves the same as Style.WrapText. The first argument of SetColumnWidth() is the column number (1-based) and the second is the width; setting the width before writing the text keeps the breaks aligned with the column, instead of leaving two or three characters dangling on each line.
Shrinking Text to Fit
When the content has to fit a fixed-width cell and the column must not grow, let the font shrink instead. ShrinkToFit lives on the style:
from spire.xls import *
from spire.xls.common import *
workbook = Workbook()
workbook.LoadFromFile("Template_Xls_1.xlsx")
sheet = workbook.Worksheets[0]
# Shrink the text so it fits the fixed width of the cell
cell = sheet.Range["B13:C13"]
style = cell.Style
style.ShrinkToFit = True
workbook.SaveToFile("ShrinkToFit.xlsx", ExcelVersion.Version2013)
workbook.Dispose()
ShrinkToFit and WrapText solve the problem in opposite directions: the first keeps one line and reduces the font size, the second keeps the font size and adds lines. For a print template whose layout must not shift, shrinking is the safer choice.
Indenting Text Inside a Cell
Indentation shifts short text horizontally within the cell, which is useful for showing hierarchy between a category and its children:
from spire.xls import *
from spire.xls.common import *
workbook = Workbook()
sheet = workbook.Worksheets[0]
cell = sheet.Range["B5"]
cell.Text = "Sub-item"
# Indent the text two levels inside the cell
cell.Style.IndentLevel = 2
workbook.SaveToFile("Indentation.xlsx", ExcelVersion.Version2010)
workbook.Dispose()
IndentLevel takes the number of levels, where 0 means no indentation. It changes only the horizontal position of the text inside the cell, not the alignment, so it composes with left alignment or centring.
Auto-Fitting Columns and Rows to Content
After wrapping is configured, the column width often needs recalculating from the longest entry. Range objects provide AutoFitColumns() and AutoFitRows():
from spire.xls import *
from spire.xls.common import *
workbook = Workbook()
workbook.LoadFromFile("ReadImages.xlsx")
sheet = workbook.Worksheets[0]
cell = sheet.Range["B8"]
cell.Text = "Welcome to Spire.XLS!"
cell.Style.Font.Size = 10
cell.Style.Font.IsBold = True
# Fit the column width and row height to the cell value
cell.AutoFitColumns()
cell.AutoFitRows()
workbook.SaveToFile("AutoFitBasedOnCellValue.xlsx", ExcelVersion.Version2013)
workbook.Dispose()
Called on a range, the fitting uses the longest and tallest content in that range, so calling it on sheet.AllocatedRange recalculates the whole sheet.
The worksheet also offers a column-index-based form for cases where only the number is known:
# Auto-fit column 2; the third argument adds extra width
sheet.AutoFitColumn(2, 2, 5)
The first two arguments are the first and last column, and the third adds a margin on top of the calculated width.
Combining the Steps: Wrap First, Fit Afterwards
In practice these steps run together: write the long text, enable wrapping, then let the dimensions settle around the content.
from spire.xls import *
from spire.xls.common import *
workbook = Workbook()
sheet = workbook.Worksheets[0]
notes = [
"Order confirmed. Payment received on 2026-10-20.",
"Address verified. Shipping scheduled for the next business day.",
"Customer requested gift wrapping.",
]
# Write the long text and enable wrapping
for i, note in enumerate(notes, start=1):
cell = sheet.Range[i + 1, 2]
cell.Value = note
cell.IsWrapText = True
# Fit after writing, when the content is final
sheet.Range[2, 2, 4, 2].AutoFitRows()
sheet.AutoFitColumn(2, 2, 3)
workbook.SaveToFile("WrapAndAutoFit.xlsx", ExcelVersion.Version2013)
workbook.Dispose()
The order matters. Fitting before wrapping computes the row height from a single line of text, and the wrapped content ends up clipped. Wrapping first and fitting afterwards lets the row height account for the extra lines.
Practical Tips
- Wrapping and indentation stack: indentation sets where the text starts, wrapping decides where it breaks, and the two do not conflict.
-
Shrink for fixed templates: when the column widths of a print template must not change,
ShrinkToFitis safer than wrapping. -
Keep URLs unwrapped: they become hard to read once broken across lines, so leave
WrapTextatFalse. -
Fit last: call
AutoFitColumns()andAutoFitRows()after content and styles are final, or the result reflects stale measurements. - Fit only the used area: auto-fitting the whole sheet repeatedly adds processing time, so restrict batch jobs to the data region.
-
Release resources: call
workbook.Dispose()after each workbook.
Conclusion
This article covered several ways to handle long text in Excel cells with Python: wrapping through Style.WrapText and IsWrapText, controlling break points with explicit \n characters and a matching column width, shrinking text inside a fixed cell width with ShrinkToFit, indenting with IndentLevel, and recalculating dimensions with AutoFitColumns(), AutoFitRows(), and AutoFitColumn(). Applied in the order "content, then style, then fitting", these settings keep generated reports tidy without any manual step.

Top comments (0)