Excel列宽转像素
Excel列宽转像素
Excel列宽是单元格中容纳的标准字体的平均字符数。就像图片宽度可以用多少像素表示,打印长度是多少磅表示,一列宽度就用标准字符个数来表示,目前列宽和像素、磅之间没有直接的转化关系,只能通过一系列近似来模拟最终的值。结合npoi、vba工具和一些实测数据,同时在一些限制条件下得出一个结论
像素 = NPOI 列宽 / 32
限制条件:1、机器dpi是96 2、excel07 3、sheet的默认字体是Calibri 11pt 4、结论是单向的,只能通过列宽转为像素 5、像素是整数,需要四舍五入,确保不使用银行家算法,Math.Round(NPOI 列宽 / 32,0,MidpointRounding.AwayFromZero)
先确认几个描述语:
a、设置列宽:在excel列上直接设置的列宽度和excel显示不一致,会加一定的边距
b、npoi列宽:通过npoi接口sheet.GetColumnWidth(colIndex)获取,单位是1/256
c、npoil列宽/256:npoi列宽/256,是标准字符数+边距,大致可以理解成标准字符数
d、vba列宽:这个是excel中直接显示的列宽,也是vba中通过ws.Columns(i).ColumnWidth获取的宽度
e、vba磅:vba中通过ws.Columns(i).Width获取的宽度,看着像是宽度转成了磅值
f、vba像素:这个是excel中直接显示的像素,也是vba磅通过换算过来的,vba磅*96/72,依赖系统dpi,这里默认96
如图下图vba宽是1.63,vba像素是18,鼠标在列边界出现可拖动形状的时候单击就显示出来了,可以省去截图工具测量。

在excel里设置了40列宽度,从0.05-2步进0.05。先读取每一列的npoi列宽和npoi列宽/256值,然后通过vba获取了列宽相关数据,同一个文件通过不同方式读取的数据见下表。
通过表格可以发现一些规律
1、npoi列宽和vba像素出现了很明显的比例关系 npoi列宽=32*vba像素,这样npoi列宽和像素就有了关系
2、vba列宽在大于等于1后突然就规律了:vba列宽+0.625=npoi列宽/256,vba列宽小于1时excel做了一定的自适应,自动将值靠向一些值,类似于分段函数的情况,这样npoi的列宽就和excel的列宽有了一定关系
3、vba磅值是0.75的倍数,进而导致了vba像素就变成了整数,通过dpi换算出来的
找到这些规律可以做什么?
1、图片定位,excel图片只能盖在单元格上,如果想居中没有对应api就只能铺满或者很近似的计算了
2、合并单元格自适应高度计算,excel合并单元格后自动换行不会调整高度,只能通过列宽值+字符长度配合计算文本大致需要几行进而计算行高来达到自适应高度的需求
| 序号 | 设置列宽 | NPOI 列宽 | NPOI列宽/256 | VBA 列宽 | VBA磅 | VBA像素 |
|---|---|---|---|---|---|---|
| 1 | 0.05 | 32 | 0.125 | 0.08 | 0.75 | 1 |
| 2 | 0.10 | 32 | 0.125 | 0.08 | 0.75 | 1 |
| 3 | 0.15 | 64 | 0.250 | 0.15 | 1.50 | 2 |
| 4 | 0.20 | 96 | 0.375 | 0.23 | 2.25 | 3 |
| 5 | 0.25 | 96 | 0.375 | 0.23 | 2.25 | 3 |
| 6 | 0.30 | 128 | 0.500 | 0.31 | 3.00 | 4 |
| 7 | 0.35 | 160 | 0.625 | 0.38 | 3.75 | 5 |
| 8 | 0.40 | 160 | 0.625 | 0.38 | 3.75 | 5 |
| 9 | 0.45 | 192 | 0.750 | 0.46 | 4.50 | 6 |
| 10 | 0.50 | 224 | 0.875 | 0.54 | 5.25 | 7 |
| 11 | 0.55 | 224 | 0.875 | 0.54 | 5.25 | 7 |
| 12 | 0.60 | 256 | 1.000 | 0.62 | 6.00 | 8 |
| 13 | 0.65 | 256 | 1.000 | 0.62 | 6.00 | 8 |
| 14 | 0.70 | 288 | 1.125 | 0.69 | 6.75 | 9 |
| 15 | 0.75 | 320 | 1.250 | 0.77 | 7.50 | 10 |
| 16 | 0.80 | 320 | 1.250 | 0.77 | 7.50 | 10 |
| 17 | 0.85 | 352 | 1.375 | 0.85 | 8.25 | 11 |
| 18 | 0.90 | 384 | 1.500 | 0.92 | 9.00 | 12 |
| 19 | 0.95 | 384 | 1.500 | 0.92 | 9.00 | 12 |
| 20 | 1.00 | 416 | 1.625 | 1.00 | 9.75 | 13 |
| 21 | 1.05 | 416 | 1.625 | 1.00 | 9.75 | 13 |
| 22 | 1.10 | 448 | 1.750 | 1.13 | 10.50 | 14 |
| 23 | 1.15 | 448 | 1.750 | 1.13 | 10.50 | 14 |
| 24 | 1.20 | 480 | 1.875 | 1.25 | 11.25 | 15 |
| 25 | 1.25 | 480 | 1.875 | 1.25 | 11.25 | 15 |
| 26 | 1.30 | 480 | 1.875 | 1.25 | 11.25 | 15 |
| 27 | 1.35 | 512 | 2.000 | 1.38 | 12.00 | 16 |
| 28 | 1.40 | 512 | 2.000 | 1.38 | 12.00 | 16 |
| 29 | 1.45 | 544 | 2.125 | 1.50 | 12.75 | 17 |
| 30 | 1.50 | 544 | 2.125 | 1.50 | 12.75 | 17 |
| 31 | 1.55 | 544 | 2.125 | 1.50 | 12.75 | 17 |
| 32 | 1.60 | 576 | 2.250 | 1.63 | 13.50 | 18 |
| 33 | 1.65 | 576 | 2.250 | 1.63 | 13.50 | 18 |
| 34 | 1.70 | 608 | 2.375 | 1.75 | 14.25 | 19 |
| 35 | 1.75 | 608 | 2.375 | 1.75 | 14.25 | 19 |
| 36 | 1.80 | 608 | 2.375 | 1.75 | 14.25 | 19 |
| 37 | 1.85 | 640 | 2.500 | 1.88 | 15.00 | 20 |
| 38 | 1.90 | 640 | 2.500 | 1.88 | 15.00 | 20 |
| 39 | 1.95 | 672 | 2.625 | 2.00 | 15.75 | 21 |
| 40 | 2.00 | 672 | 2.625 | 2.00 | 15.75 | 21 |
点击查看代码
static void ReadExcel()
{
using (FileStream fs = new FileStream("testw.xlsx", FileMode.Open, FileAccess.Read))
{
IWorkbook wb = new XSSFWorkbook(fs);
ISheet sheet = wb.GetSheetAt(0);
var sheet1=wb.GetSheetAt(1);
Enumerable.Range(0,40).ToList().ForEach(r=>
{
var cellColumnWidth = sheet.GetColumnWidth(r);
Console.WriteLine($"第{r+1}npoi列宽:{cellColumnWidth},npoi列宽/256:{cellColumnWidth/256f}");
});
}
}
点击查看代码
Sub SetAndGetColumnWidths()
Dim ws As Worksheet
Set ws = ActiveSheet
Dim i As Integer
Dim designWidth As Double
Dim actualVbaWidth As Double
Dim points As Double
Dim pixels As Double
' 清空前几行(可选)
ws.Range("A1:E1").Value = Array("列", "设置列宽", "vba列宽", "vba磅", "vba像素")
For i = 1 To 40
' 1. 设置设计宽度:0.05, 0.10, ..., 2.00
designWidth = 0.05 * i
' 设置列宽(Excel 会自动量化到最近可表示值)
ws.Columns(i).ColumnWidth = designWidth
' 2. 读取实际 VBA 列宽(可能 ≠ designWidth)
actualVbaWidth = ws.Columns(i).ColumnWidth
' 3. 获取 .Width(单位:磅)
points = ws.Columns(i).Width
' 4. 转换为像素(96 DPI: 1 pt = 96/72 = 4/3 px)
pixels = points * 96 / 72 ' = points * 1.333...
' 5. 输出到 Immediate Window
Debug.Print "列 " & i & " (" & Split(Cells(1, i).Address, "$")(1) & "): " & _
"设置列宽=" & Format(designWidth, "0.00") & ", " & _
"vba列宽=" & Format(actualVbaWidth, "0.00") & ", " & _
"vba磅=" & Format(points, "0.00") & ", " & _
"vba像素=" & Format(pixels, "0.00")
' 6. 可选:写入工作表(从第2行开始)
ws.Cells(i + 1, 1).Value = Split(Cells(1, i).Address, "$")(1) ' 列字母
ws.Cells(i + 1, 2).Value = designWidth
ws.Cells(i + 1, 3).Value = actualVbaWidth
ws.Cells(i + 1, 4).Value = points
ws.Cells(i + 1, 5).Value = pixels
Next i
MsgBox "完成!共处理 40 列。结果已输出到 Immediate Window 和工作表。"
End Sub
引用文件:
列宽:https://support.microsoft.com/zh-cn/office/更改列宽和行高-72f5e3cc-994d-43e8-ae58-9774a0905f46

浙公网安备 33010602011771号