vb macro string width in pixel

前端 未结 9 1508
感动是毒
感动是毒 2020-12-09 06:25

How would you calculate the number of pixels for a String (in an arbitrary font), using an Excel VBA macro?

Related:

  • http://www.mrexcel.com/forum/excel
相关标签:
9条回答
  • 2020-12-09 07:03

    Write a new module class and put the following code in it.

    'Option Explicit
    
    'API Declares
    
    Private Declare Function CreateDC Lib "gdi32.dll" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, lpInitData As Long) As Long
    Private Declare Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
    Private Declare Function CreateFontIndirect Lib "gdi32.dll" Alias "CreateFontIndirectA" (lpLogFont As LOGFONT) As Long
    Private Declare Function SelectObject Lib "gdi32.dll" (ByVal hdc As Long, ByVal hObject As Long) As Long
    Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long
    Private Declare Function GetTextExtentPoint32 Lib "gdi32.dll" Alias "GetTextExtentPoint32A" (ByVal hdc As Long, ByVal lpsz As String, ByVal cbString As Long, lpSize As SIZE) As Long
    Private Declare Function MulDiv Lib "kernel32.dll" (ByVal nNumber As Long, ByVal nNumerator As Long, ByVal nDenominator As Long) As Long
    Private Declare Function GetDC Lib "user32.dll" (ByVal hwnd As Long) As Long
    Private Declare Function GetDeviceCaps Lib "gdi32.dll" (ByVal hdc As Long, ByVal nIndex As Long) As Long
    Private Declare Function DeleteDC Lib "gdi32.dll" (ByVal hdc As Long) As Long
    
    Private Const LOGPIXELSY As Long = 90
    
    Private Type LOGFONT
        lfHeight As Long
        lfWidth As Long
        lfEscapement As Long
        lfOrientation As Long
        lfWeight As Long
        lfItalic As Byte
        lfUnderline As Byte
        lfStrikeOut As Byte
        lfCharSet As Byte
        lfOutPrecision As Byte
        lfClipPrecision As Byte
        lfQuality As Byte
        lfPitchAndFamily As Byte
        lfFaceName As String * 32
    End Type
    
    Private Type SIZE
        cx As Long
        cy As Long
    End Type
    Public Function getLabelPixel(label As String) As Integer
    
      Dim font As New StdFont
      Dim sz As SIZE
      font.Name = "Arial Narrow"
      font.SIZE = 9.5
    
      sz = GetLabelSize(label, font)
      getLabelPixel = sz.cx
    
    End Function
    
    Private Function GetLabelSize(text As String, font As StdFont) As SIZE
        Dim tempDC As Long
        Dim tempBMP As Long
        Dim f As Long
        Dim lf As LOGFONT
        Dim textSize As SIZE
    
        ' Create a device context and a bitmap that can be used to store a
        ' temporary font object
        tempDC = CreateDC("DISPLAY", vbNullString, vbNullString, ByVal 0)
        tempBMP = CreateCompatibleBitmap(tempDC, 1, 1)
    
        ' Assign the bitmap to the device context
        DeleteObject SelectObject(tempDC, tempBMP)
    
        ' Set up the LOGFONT structure and create the font
        lf.lfFaceName = font.Name & Chr$(0)
        lf.lfHeight = -MulDiv(font.SIZE, GetDeviceCaps(GetDC(0), 90), 72) 'LOGPIXELSY
        lf.lfItalic = font.Italic
        lf.lfStrikeOut = font.Strikethrough
        lf.lfUnderline = font.Underline
        If font.Bold Then lf.lfWeight = 800 Else lf.lfWeight = 400
        f = CreateFontIndirect(lf)
    
        ' Assign the font to the device context
        DeleteObject SelectObject(tempDC, f)
    
        ' Measure the text, and return it into the textSize SIZE structure
        GetTextExtentPoint32 tempDC, text, Len(text), textSize
    
        ' Clean up (very important to avoid memory leaks!)
        DeleteObject f
        DeleteObject tempBMP
        DeleteDC tempDC
      ' Return the measurements
        GetLabelSize = textSize
    
    End Function
    

    Call the getLabelPixel function with parameter(string whose width has to be calculated).

    0 讨论(0)
  • 2020-12-09 07:06

    User 1355's answer is excellent! (I would have put that in the comments, but my reputation is not high enough... yet.)

    I'm not measuring labels, but text within a cell and I didn't want to make assumptions about the font, so I made some minor modifications and additions.

    As instructed by 1355, Write a new module class and put the following code in it.

    'Option Explicit
    
    'API Declares
    
    Private Declare Function CreateDC Lib "gdi32.dll" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, lpInitData As Long) As Long
    Private Declare Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
    Private Declare Function CreateFontIndirect Lib "gdi32.dll" Alias "CreateFontIndirectA" (lpLogFont As LOGFONT) As Long
    Private Declare Function SelectObject Lib "gdi32.dll" (ByVal hdc As Long, ByVal hObject As Long) As Long
    Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long
    Private Declare Function GetTextExtentPoint32 Lib "gdi32.dll" Alias "GetTextExtentPoint32A" (ByVal hdc As Long, ByVal lpsz As String, ByVal cbString As Long, lpSize As FNTSIZE) As Long
    Private Declare Function MulDiv Lib "kernel32.dll" (ByVal nNumber As Long, ByVal nNumerator As Long, ByVal nDenominator As Long) As Long
    Private Declare Function GetDC Lib "user32.dll" (ByVal hwnd As Long) As Long
    Private Declare Function GetDeviceCaps Lib "gdi32.dll" (ByVal hdc As Long, ByVal nIndex As Long) As Long
    Private Declare Function DeleteDC Lib "gdi32.dll" (ByVal hdc As Long) As Long
    
    Private Const LOGPIXELSY As Long = 90
    
    Private Type LOGFONT
        lfHeight As Long
        lfWidth As Long
        lfEscapement As Long
        lfOrientation As Long
        lfWeight As Long
        lfItalic As Byte
        lfUnderline As Byte
        lfStrikeOut As Byte
        lfCharSet As Byte
        lfOutPrecision As Byte
        lfClipPrecision As Byte
        lfQuality As Byte
        lfPitchAndFamily As Byte
        lfFaceName As String * 32
    End Type
    
    Private Type FNTSIZE
        cx As Long
        cy As Long
    End Type
    
    
    Public Function GetLabelPixelWidth(label As String) As Integer
    
        Dim font As New StdFont
        Dim sz As FNTSIZE
        font.Name = "Arial Narrow"
        font.Size = 9.5
    
        sz = GetLabelSize(label, font)
        getLabelPixelWidth = sz.cx
    
    End Function
    
    
    Public Function GetStringPixelHeight(text As String, fontName As String, fontSize As Single, Optional isBold As Boolean = False, Optional isItalics As Boolean = False) As Integer
    
        Dim font As New StdFont
        Dim sz As FNTSIZE
        font.Name = fontName
        font.Size = fontSize
        font.Bold = isBold
        font.Italic = isItalics
    
        sz = GetLabelSize(text, font)
        GetStringPixelWidth = sz.cy
    
    End Function
    
    
    Public Function GetStringPixelWidth(text As String, fontName As String, fontSize As Single, Optional isBold As Boolean = False, Optional isItalics As Boolean = False) As Integer
    
        Dim font As New StdFont
        Dim sz As FNTSIZE
        font.Name = fontName
        font.Size = fontSize
        font.Bold = isBold
        font.Italic = isItalics
    
        sz = GetLabelSize(text, font)
        GetStringPixelWidth = sz.cx
    
    End Function
    
    
    Private Function GetLabelSize(text As String, font As StdFont) As FNTSIZE
        Dim tempDC As Long
        Dim tempBMP As Long
        Dim f As Long
        Dim lf As LOGFONT
        Dim textSize As FNTSIZE
    
        ' Create a device context and a bitmap that can be used to store a
        ' temporary font object
        tempDC = CreateDC("DISPLAY", vbNullString, vbNullString, ByVal 0)
        tempBMP = CreateCompatibleBitmap(tempDC, 1, 1)
    
        ' Assign the bitmap to the device context
        DeleteObject SelectObject(tempDC, tempBMP)
    
        ' Set up the LOGFONT structure and create the font
        lf.lfFaceName = font.Name & Chr$(0)
        lf.lfHeight = -MulDiv(font.Size, GetDeviceCaps(GetDC(0), 90), 72) 'LOGPIXELSY
        lf.lfItalic = font.Italic
        lf.lfStrikeOut = font.Strikethrough
        lf.lfUnderline = font.Underline
        If font.Bold Then lf.lfWeight = 800 Else lf.lfWeight = 400
        f = CreateFontIndirect(lf)
    
        ' Assign the font to the device context
        DeleteObject SelectObject(tempDC, f)
    
        ' Measure the text, and return it into the textSize SIZE structure
        GetTextExtentPoint32 tempDC, text, Len(text), textSize
    
        ' Clean up (very important to avoid memory leaks!)
        DeleteObject f
        DeleteObject tempBMP
        DeleteDC tempDC
        ' Return the measurements
        GetLabelSize = textSize
    
    End Function
    

    Some examples of calling the GetStringPixelWidth function

    MsgBox (GetStringPixelWidth("Test String", "Calibri", 10))
    MsgBox (GetStringPixelWidth(" ", "Calibri", 10, True, False))
    

    Thanks again to 1355 for saving me tons of work!

    0 讨论(0)
  • 2020-12-09 07:11

    To expand on and hone Dustin's answer, here is the code that I use.

    Like Dustin, I have a label on a hidden user form with AutoSize = True. Make sure WordWrap = False or else you get bizarre results;)

    However, there is a bit of extra fluff added onto the label's width each time. To correct for it, you need to also find the width of an blank caption and subtract the difference. Even that is problematic sometimes so in my code I find the difference between the string plus an arbitrary character and the arbitrary character by itself.

    The following code can go in any module you like. frmTextWidth is the name of the custom form and Label1 is the label that will measure the width of text.

    Public Function TextWidth(ByVal Text As Variant, _
                     Optional ByVal FontName As Variant, _
                     Optional FontSize As Double) As Single
    
      If TypeName(Text) = "Range" Then
        If IsMissing(FontName) Then Set FontName = Text
        Text = Text.Value
      End If
    
      If TypeName(FontName) = "Range" Then
        frmTextWidth.Label1.Font = FontName.Font
      ElseIf VarType(FontName) = vbString Then
        If FontName <> "" Then frmTextWidth.Label1.Font.Name = FontName
        If FontSize <> 0 Then frmTextWidth.Label1.Font.Size = FontSize
      End If      
    
      frmTextWidth.Label1.Caption = CStr(Text) + "."
      TextWidth = frmTextWidth.Label1.Width
    
      frmTextWidth.Label1.Caption = "."
      TextWidth = TextWidth - frmTextWidth.Label1.Width
    
    End Function
    

    You can supply a range as the string source and the function will automatically pick up the string and its font. If you have a string in a cell that has mixed fonts and font sizes, you can understand that this function won't work. You would have to find the size of each individual formated character but the code involved is not too tricky.

    If you call the function allot, you may not want to set the font of the label every time because it will bog down the function. Simply test to see if the requested font name/size is different than what Label1 is set to before changing it.

    0 讨论(0)
提交回复
热议问题