“一个单位的列宽等于 Normal 样式中一个字符的宽度。对于比例字体,使用字符 0(零)的宽度。”
因此ColumnWidth
,在 Excel 中,以适合一列的“0”字符数来衡量。如何将此值转换为像素,反之亦然?
“一个单位的列宽等于 Normal 样式中一个字符的宽度。对于比例字体,使用字符 0(零)的宽度。”
因此ColumnWidth
,在 Excel 中,以适合一列的“0”字符数来衡量。如何将此值转换为像素,反之亦然?
如前所述ColumnWidth
,Excel 中的值取决于工作簿的默认字体,可以通过Workbook.Styles("Normal").Font
. 它还取决于当前屏幕 DPI。
在 Excel 2013 中对不同字体和大小进行了一些研究后,我发现我们有 2 个线性函数(Arial 无法看到,因为它与 Tahoma 重叠。):
从图中可以看出,该功能ColumnWidth < 1
与折线图的主要部分不同。它被计算为一列中的像素数/在一列中适合一个“0”字符所需的像素数。
现在让我们看看典型的单元格宽度是由什么组成的。
A
- 普通样式中的“0”字符宽度B
- 左右填充C
- 1px 右边距A
可以用GetTextExtentPoint32 Windows API 函数计算,但字体大小要大一点。通过实验,我选择了 +0.3pt,它适用于基本大小为 8-48pt 的不同字体。使用"round half up"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)