在MS Excel中,GetTextExtentPoint32W返回的单元格中文本的文本宽度大于实际宽度

时间:2014-12-20 06:32:23

标签: python-2.7 excel-vba ctypes vba excel

我正在使用GetTextExtentPoint32W获取MS Excel 2010中单元格中文本的宽度。使用ActiveCell.Width获取单元格宽度。然后比较这两个宽度以确定文本是否适合细胞或延伸出细胞。 在视觉上,即使文本完全适合单元格,该方法返回的文本宽度也大于单元格宽度。此外,当我增加字体大小时,实际文本宽度与方法返回的宽度之间的差异会增加。 以下是用于实现结果的源代码的一部分。请帮我解决这个错误。

    hDC = ctypes.windll.user32.GetDC(self.windowHandle)
    tempBMP = ctypes.windll.gdi32.CreateCompatibleBitmap(hDC, 1, 1)
    hBMP = ctypes.windll.gdi32.SelectObject(hDC, tempBMP)

    iFontSize = self.excelCellObject.Font.Size
    deviceCaps = ctypes.windll.gdi32.GetDeviceCaps(hDC, 90) 
    iFontSize = int(iFontSize)
    iFontSize = ctypes.c_int(iFontSize)
    iFontSize = ctypes.windll.kernel32.MulDiv(iFontSize, deviceCaps, 72)
    iFontSize = iFontSize * -1
    iFontWeight = 700 if self.excelCellObject.Font.Bold else 400

    sFontName = self.excelCellObject.Font.Name
    sFontItalic = self.excelCellObject.Font.Italic
    sFontUnderline = True if self.excelCellObject.Font.Underline else False
    sFontStrikeThrough = self.excelCellObject.Font.Strikethrough

    #Create a font object with the correct size, weight and style
    hFont = ctypes.windll.gdi32.CreateFontW(iFontSize, 
                                            0, 0, 0, 
                                            iFontWeight, 
                                            sFontItalic, 
                                            sFontUnderline, 
                                            sFontStrikeThrough, 
                                            False, False, False, 
                                            False, False,
                                            sFontName)

    #Load the font into the device context, storing the original font object
    hOldFont = ctypes.windll.gdi32.SelectObject(hDC, hFont)
    sText = self.excelCellObject.Text
    log.io("\nText \t"+sText+"\n")
    textLength = len(sText)

    class structText(ctypes.Structure):
        _fields_ = [("width", ctypes.c_int), 
                    ("height",ctypes.c_int)]

    StructText = structText()
    getTextExtentPoint = ctypes.windll.gdi32.GetTextExtentPoint32W
    getTextExtentPoint.argtypes = [ctypes.c_void_p, 
                                   ctypes.c_char_p, 
                                   ctypes.c_int, 
                                   ctypes.POINTER(structText)]
    getTextExtentPoint.restype = ctypes.c_int

    #Get the text dimensions
    a = ctypes.windll.gdi32.GetTextExtentPoint32W(hDC, 
                                                  sText, 
                                                  textLength,
                                                  ctypes.byref(StructText))

    #Delete the font object we created
    a = ctypes.windll.gdi32.DeleteObject(hFont)
    a = ctypes.windll.gdi32.DeleteObject(tempBMP)

    #Release the device context
    a = ctypes.windll.user32.ReleaseDC(self.windowHandle, hDC)
    textWidth = StructText.width
    cellWidth = self.excelCellObject.Width

感谢。

1 个答案:

答案 0 :(得分:0)

我不使用Python或Excel 2010,因此无法评论您当前的方法。但是,我一直在努力解决类似的问题。我希望以下几点对您有所帮助。


<强>背景

如果将鼠标悬停在Excel列的右边界并按住鼠标左键,将显示以下格式:“宽度:n.nn(mm像素)”。

ColumnWidth属性的帮助说:

  

一个单位的列宽等于一个字符的宽度   正常风格。对于比例字体,字符0的宽度   使用(零)。

     

使用Width属性以磅为单位返回列的宽度。

据我所知,“正常样式”表示创建工作簿时的标准字体名称和大小。更改现有工作簿的标准字体名称和大小似乎没有任何效果。更改工作表的字体名称和大小无效。

标准宽度列的两个示例显示为:

For Arial 10         Width: 8.43 (64 pixels)
For Tahoma 10.5      Width: 8.38 (72 pixels)

我创建了一个零字符串,并尝试根据列的宽度来测量可见的数量。我发现我能看到的零的数量与显示的值相匹配,例如主观测量。

对于VBA,列或单元格的ColumnWidth属性设置或返回字符宽度。

对于VBA,列或单元格的只读Width属性返回.75 *宽度(以像素为单位)。

以上信息的重要性在于,从Excel获得的宽度值对于所使用的字体不一定正确。


我的问题和我发现的解决方案

我遇到的问题是我正在合并单元格并用文本填充它们。虽然Excel将调整行的高度,以便在未合并的单元格中显示文本,但对于合并的单元格,它不会这样做。我尝试了很多技术,包括微软的.Net,文本渲染程序都没有成功。我没有尝试过任何可靠的模拟Excel系统来确定文本的宽度。

我最终成功使用的技术包括在所有使用的细胞的右侧和下方挑选细胞用于实验。我调整了实验单元格列的宽度以匹配合并单元格的组合宽度,并复制了我想要的高度的单元格的格式化值。由于实验细胞未合并,Excel会根据需要调整高度。然后我确定源行至少是这个高度。

这项技术的关键特征是我没有尝试模拟Excel的系统来确定文本的宽度;我使用的是Excel的系统。

您需要为实验细胞尝试不同的色谱柱宽度。我将从源列的当前宽度开始。如果行高与单行的高度匹配,则源列的宽度已足够。如果行高度超过单行的行高,我会增加列宽,直到行高与单行匹配,然后调整源列的宽度以匹配。如果您从最宽的值(字符或Python的文本宽度)开始,您可能会在第一次尝试时获得正确的源列宽度,这样可以避免以后需要进行调整。