Как уже упоминалось, значение ColumnWidth
в Excel зависит от шрифта рабочей книги по умолчанию, который можно получить с помощью Workbook.Styles("Normal").Font
. Кроме того, это зависит от текущего разрешения экрана.
Проведя некоторые исследования для различных шрифтов и размеров в Excel 2013, я обнаружил, что у нас есть 2 линейные функции (Arial нельзя увидеть, поскольку он перекрывается с Tahoma.) :

Как видно на рисунке, функция для ColumnWidth < 1
отличается от основной части линейного графика. Он рассчитывается как количество пикселей в столбце / количество пикселей, необходимое для размещения одного символа «0» в столбце.
Теперь давайте посмотрим, из чего состоит типичная ширина ячейки.

A
- ширина символа "0" в обычном стиле B
- отступы слева и справа C
- 1px правое поле
A
можно рассчитать с помощью GetTextExtentPoint32 Windows API-функции, но размер шрифта должен быть немного больше. Экспериментально я выбрал + 0.3pt, который работал для разных шрифтов с базовым размером 8-48pt. B
- (A + 1) / 4
округляется до целого числа, используя "округлить до половины" . Также здесь потребуется экран DPI (см. Реализацию Python 3 ниже)
Вот уравнения для преобразования символов в пиксели и их реализация в Python 3:



import win32print, win32gui
from math import floor
def get_screen_dpi():
dc = win32gui.GetDC(0)
LOGPIXELSX, LOGPIXELSY = 88, 90
dpi = [win32print.GetDeviceCaps(dc, i) for i in (LOGPIXELSX,
LOGPIXELSY)]
win32gui.ReleaseDC(0, dc)
return dpi
def get_text_metrics(fontname, fontsize):
"Measures '0' char size for the specified font name and size in pt"
dc = win32gui.GetDC(0)
font = win32gui.LOGFONT()
font.lfFaceName = fontname
font.lfHeight = -fontsize * dpi[1] / 72
hfont = win32gui.CreateFontIndirect(font)
win32gui.SelectObject(dc, hfont)
metrics = win32gui.GetTextExtentPoint32(dc, "0")
win32gui.ReleaseDC(0, dc)
return metrics
def ch_px(v, unit="ch"):
"""
Convert between Excel character width and pixel width.
`unit` - unit to convert from: 'ch' (default) or 'px'
"""
rd = lambda x: floor(x + 0.5) # round half up
# pad = left cell padding + right cell padding + cell border(1)
pad = rd((z + 1) / 4) * 2 + 1
z_p = z + pad # space (px) for "0" character with padding
if unit == "ch":
return v * z_p if v < 1 else v * z + pad
else:
return v / z_p if v < z_p else (v - pad) / z
font = "Calibri", 11
dpi = get_screen_dpi()
z = get_text_metrics(font[0], font[1] + 0.3)[0] # "0" char width in px
px = ch_px(30, "ch")
ch = ch_px(px, "px")
print("Characters:", ch, "Pixels:", px, "for", font)