在字符单位和像素(点)之间转换Excel列宽
Convert Excel column width between characters unit and pixels (points)
因此 Excel 中的 ColumnWidth
被测量为适合一列的“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 function, but font size should be a little bit bigger. By experiment I chose +0.3pt which worked for me for different fonts with 8-48pt base size. B
is (A + 1) / 4
rounded to integer using "round half up"计算出来。此处还需要屏幕 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)
2022 年仍然是同样的问题...发现线程可以追溯到 2010 年有问题...
开始于:像素 != 点
点定义为 72 点/英寸:https://docs.microsoft.com/en-us/office/vba/language/glossary/vbe-glossary#point
虽然这个定义看起来很愚蠢,因为一个固定宽度为 100 点的形状,无论显示器配置如何,都会在每台显示器上显示完全相同的尺寸(以英寸为单位),但事实并非如此。
字符数是定义为默认文本格式的0字符数的单位。设置为10个字符宽度的单元格,当单元格内容格式化为默认格式时,可以容纳10个“0”字符。
我的情况是我需要将图片放入文档并将文本放入旁边的单元格中。但是图片悬停在文档上,单元格隐藏在它下面。根据图片的大小,或多或少的单元格被隐藏。因此,我不能只说我将文本 5 个单元格放在图片的左侧。将列自动调整为列单元格的内容,不考虑悬停图片。
图片绑定到图片左上角下方的单元格。我需要将该单元格的大小设置为图片的大小才能解决问题。
一张图片就是一个形状。形状 returns 其宽度为 点 (Shape.Width
).
可以将范围设置为像 Worksheet.Range["A1"]
这样的单元格。从 Range 中,您可以获得 Characters (Range.ColumnWidth
) 或 Points (Range.Width
) 的宽度。但是你只能在 Characters (Range.ColumnWidth
).
中设置 Range 的宽度
所以我们可以在点中检索图片(形状)的大小,需要将它们转换为字符来设置单元格到正确的宽度...
一些研究表明,单元格的点大小包含一个间距常数(单元格内容前后的填充),可能还有单元格之间的分隔线。
在我的系统上:
A cell set to a width of 1 **Characters** = 9 **Points**
A cell set to a width of 2 **Characters** = 14.25 **Points**
A cell set to a width of 3 **Characters** = 19.5 **Points**
正如我所说,积分中有一个常数。因此,从 1 个字符 到 2 个字符 ,区别仅在于字母的大小。
SizeOfLetter = 14.25 点 - 9 点 = 5.25 点
然后我们可以从 Points 中减去 1 Characters 的 SizeOfLetter 并得到 Points常数。
PointsConstant = 9 Points - 5.25 Points = 3.75 Points
验证:
包含 3 个“0”字母的单元格的点大小 = 3SizeOfLetter + PointsConstant = 35.25 Points + 3.75 Points = 19.5 分
由于值取决于您的系统,您不能使用这些值!
最好的方法是使用代码为您的系统计算它:
C#代码:
Excel.Application excelApp = new Excel.Application();
Excel.Workbook workbook1 = excelApp.Workbooks.Add();
Excel.Worksheet sheet1 = (Excel.Worksheet)workbook1.ActiveSheet;
// Evaluate the Points data for the document
double previousColumnWidth = (double)sheet1.Range["A1"].ColumnWidth;
sheet1.Range["A1"].ColumnWidth = 1; // Make the cell fit 1 character
double points1 = (double)sheet1.Range["A1"].Width;
sheet1.Range["A1"].ColumnWidth = 2; // Make the cell fit 2 characters
double points2 = (double)sheet1.Range["A1"].Width;
double SizeOfLetter = points2 - points1;
double PointsConstant = points1 - pointsPerCharater;
// Reset the column width
sheet1.Range["A1"].ColumnWidth = previousColumnWidth;
// Create a function for the conversion
Func<double, double> PointsToCharacters = (double points) => (points - PointsConstant ) / SizeOfLetter ;
因此 Excel 中的 ColumnWidth
被测量为适合一列的“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 function, but font size should be a little bit bigger. By experiment I chose +0.3pt which worked for me for different fonts with 8-48pt base size. B
is (A + 1) / 4
rounded to integer using "round half up"计算出来。此处还需要屏幕 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)
2022 年仍然是同样的问题...发现线程可以追溯到 2010 年有问题...
开始于:像素 != 点
点定义为 72 点/英寸:https://docs.microsoft.com/en-us/office/vba/language/glossary/vbe-glossary#point
虽然这个定义看起来很愚蠢,因为一个固定宽度为 100 点的形状,无论显示器配置如何,都会在每台显示器上显示完全相同的尺寸(以英寸为单位),但事实并非如此。
字符数是定义为默认文本格式的0字符数的单位。设置为10个字符宽度的单元格,当单元格内容格式化为默认格式时,可以容纳10个“0”字符。
我的情况是我需要将图片放入文档并将文本放入旁边的单元格中。但是图片悬停在文档上,单元格隐藏在它下面。根据图片的大小,或多或少的单元格被隐藏。因此,我不能只说我将文本 5 个单元格放在图片的左侧。将列自动调整为列单元格的内容,不考虑悬停图片。 图片绑定到图片左上角下方的单元格。我需要将该单元格的大小设置为图片的大小才能解决问题。
一张图片就是一个形状。形状 returns 其宽度为 点 (Shape.Width
).
可以将范围设置为像 Worksheet.Range["A1"]
这样的单元格。从 Range 中,您可以获得 Characters (Range.ColumnWidth
) 或 Points (Range.Width
) 的宽度。但是你只能在 Characters (Range.ColumnWidth
).
所以我们可以在点中检索图片(形状)的大小,需要将它们转换为字符来设置单元格到正确的宽度...
一些研究表明,单元格的点大小包含一个间距常数(单元格内容前后的填充),可能还有单元格之间的分隔线。
在我的系统上:
A cell set to a width of 1 **Characters** = 9 **Points**
A cell set to a width of 2 **Characters** = 14.25 **Points**
A cell set to a width of 3 **Characters** = 19.5 **Points**
正如我所说,积分中有一个常数。因此,从 1 个字符 到 2 个字符 ,区别仅在于字母的大小。
SizeOfLetter = 14.25 点 - 9 点 = 5.25 点
然后我们可以从 Points 中减去 1 Characters 的 SizeOfLetter 并得到 Points常数。
PointsConstant = 9 Points - 5.25 Points = 3.75 Points
验证: 包含 3 个“0”字母的单元格的点大小 = 3SizeOfLetter + PointsConstant = 35.25 Points + 3.75 Points = 19.5 分
由于值取决于您的系统,您不能使用这些值!
最好的方法是使用代码为您的系统计算它:
C#代码:
Excel.Application excelApp = new Excel.Application();
Excel.Workbook workbook1 = excelApp.Workbooks.Add();
Excel.Worksheet sheet1 = (Excel.Worksheet)workbook1.ActiveSheet;
// Evaluate the Points data for the document
double previousColumnWidth = (double)sheet1.Range["A1"].ColumnWidth;
sheet1.Range["A1"].ColumnWidth = 1; // Make the cell fit 1 character
double points1 = (double)sheet1.Range["A1"].Width;
sheet1.Range["A1"].ColumnWidth = 2; // Make the cell fit 2 characters
double points2 = (double)sheet1.Range["A1"].Width;
double SizeOfLetter = points2 - points1;
double PointsConstant = points1 - pointsPerCharater;
// Reset the column width
sheet1.Range["A1"].ColumnWidth = previousColumnWidth;
// Create a function for the conversion
Func<double, double> PointsToCharacters = (double points) => (points - PointsConstant ) / SizeOfLetter ;