|
#!/usr/bin/env python3 |
|
|
|
import argparse |
|
from pathlib import Path |
|
|
|
from openpyxl import load_workbook |
|
from openpyxl.utils import get_column_letter |
|
|
|
|
|
def displayed_length(value: object) -> int: |
|
"""Estimate the number of characters needed to display a cell value.""" |
|
if value is None: |
|
return 0 |
|
|
|
# Handle cells containing multiple lines. |
|
return max(len(line) for line in str(value).splitlines()) |
|
|
|
|
|
def autofit_worksheet( |
|
worksheet, |
|
*, |
|
padding: int = 2, |
|
minimum_width: int = 8, |
|
maximum_width: int = 120, |
|
) -> None: |
|
"""Adjust every populated column to approximately fit its contents.""" |
|
|
|
for column_cells in worksheet.iter_cols(): |
|
maximum_length = 0 |
|
|
|
for cell in column_cells: |
|
# Ignore non-anchor cells belonging to merged ranges. |
|
if cell.__class__.__name__ == "MergedCell": |
|
continue |
|
|
|
maximum_length = max( |
|
maximum_length, |
|
displayed_length(cell.value), |
|
) |
|
|
|
if maximum_length == 0: |
|
continue |
|
|
|
width = maximum_length + padding |
|
width = max(minimum_width, min(width, maximum_width)) |
|
|
|
column_letter = get_column_letter(column_cells[0].column) |
|
worksheet.column_dimensions[column_letter].width = width |
|
|
|
|
|
def autofit_workbook(filename: Path) -> None: |
|
workbook = load_workbook(filename) |
|
|
|
for worksheet in workbook.worksheets: |
|
print(f"Adjusting sheet: {worksheet.title}") |
|
autofit_worksheet(worksheet) |
|
|
|
workbook.save(filename) |
|
print(f"Saved: {filename}") |
|
|
|
|
|
def main() -> None: |
|
parser = argparse.ArgumentParser( |
|
description="Adjust Excel column widths to fit their contents." |
|
) |
|
parser.add_argument( |
|
"files", |
|
nargs="+", |
|
type=Path, |
|
help="Excel workbook files to modify in place", |
|
) |
|
|
|
args = parser.parse_args() |
|
|
|
for filename in args.files: |
|
if not filename.is_file(): |
|
print(f"File not found: {filename}") |
|
continue |
|
|
|
if filename.suffix.lower() not in {".xlsx", ".xlsm"}: |
|
print(f"Skipping unsupported file: {filename}") |
|
continue |
|
|
|
try: |
|
autofit_workbook(filename) |
|
except Exception as error: |
|
print(f"Failed to process {filename}: {error}") |
|
|
|
|
|
if __name__ == "__main__": |
|
main() |