Jump to content

GetTabbedStringSizeEx()


Yashied
 Share

Recommended Posts

Many people on the forum wrote a functions to calculate the dimensions of a string, but I have not found a solution if the string contains one or more tab characters (at best, replaced @TAB by 4 or more other characters). All use the GetTextExtentPoint32() function, but there is GetTabbedTextExtent() that supports the tabbed strings. There's only one problem using it in AutoIt. We need to specify the tab-stop positions in pixels (see "nTabPositions" and "lpnTabStopPositions" parameters). If it is incorrect then the result will be wrong. The value of tab-stop position should be strictly the same that use AutoIt to draw the tabbed strings. But the difficulty is that this value depends on the typeface and size of the used font. After some experimentation, I came to the conclusion that the value of tab-stop position can be calculated as follows:

 

8 * TEXTMETRIC("tmAveCharWidth")

So, the following function returns the dimensions of a tabbed or non-tabbed string. Moreover, the string can contain carriage returns (@CR or @CRLF). Additionally, you can align the string through a tab characters to display it as a table in the Label control (see screenshot). I tested the example which is shown below with a few fonts and it works fine.

 

post_img_092.png

#Include <GUIConstants.au3>
#Include <WindowsConstants.au3>

Opt('MouseCoordMode', 2)

Global $Text = '#1'               & @TAB & '0B7' & @TAB & '#2'                 & @TAB & '01F' & @TAB & '#3'                 & @TAB & '2FF' & @TAB & '#4'               & @TAB & '077' & @CRLF & _
               'COMPRESS_COPY'    & @TAB & '+'   & @TAB & 'PREFIX_PLATFORM'    & @TAB & '+'   & @TAB & 'TEST'               & @TAB & '+'   & @TAB & 'WIN7_PROGRESSBAR' & @TAB & '+'   & @CRLF & _
               'COMPRESS_BCJ'     & @TAB & '+'   & @TAB & 'PREFIX_WAITALL'     & @TAB & '+'   & @TAB & 'LANG'               & @TAB & '+'   & @TAB & 'RTF_CONTROL'      & @TAB & '+'   & @CRLF & _
               'COMPRESS_BCJ2'    & @TAB & '+'   & @TAB & 'PREFIX_HIDCON'      & @TAB & '+'   & @TAB & 'ELEVATION'          & @TAB & '+'   & @TAB & 'IMAGES'           & @TAB & '+'   & @CRLF & _
               'COMPRESS_DEFLATE' & @TAB & '-'   & @TAB & 'PREFIX_NOWAIT'      & @TAB & '+'   & @TAB & 'CHECK_RAM'          & @TAB & '+'   & @CRLF & _
               'COMPRESS_LZMA'    & @TAB & '+'   & @TAB & 'PREFIX_FORCENOWAIT' & @TAB & '+'   & @TAB & 'CHECK_FREE_SPACE'   & @TAB & '+'   & @CRLF & _
               'COMPRESS_LZMA2'   & @TAB & '+'   & @TAB & ''                   & @TAB & ''    & @TAB & 'BEGINPROMPTTIMEOUT' & @TAB & '+'   & @CRLF & _
               'COMPRESS_PPMD'    & @TAB & '-'   & @TAB & ''                   & @TAB & ''    & @TAB & 'CONFIG_PLATFORM'    & @TAB & '+'   & @CRLF & _
               'CRYPTO'           & @TAB & '+'   & @TAB & ''                   & @TAB & ''    & @TAB & 'EARLY_PASSWORD'     & @TAB & '+'   & @CRLF & _
               'VOLUMES'          & @TAB & '-'   & @TAB & ''                   & @TAB & ''    & @TAB & 'VOLUME_NAME_STYLE'  & @TAB & '-'   & @CRLF & _
               'PROTECT'          & @TAB & '-'   & @TAB & ''                   & @TAB & ''    & @TAB & 'ENVIRONMENT_VARS'   & @TAB & '+'

Global $Font[20] = [1.5, 2.3, 3.0, 3.8, 4.5, 5.3, 6.0, 6.8, 7.5, 8.3, 9.0, 9.8, 10.5, 11.3, 12.0, 12.8, 13.5, 14.3, 15.0, 15.8]

For $i = 0 To UBound($Font) - 1
    _Form($Text, 'Segoe UI', $Font[$i])
Next

Func _Form($sText, $sFont, $nFont)

    Local $hForm, $Button, $Pos, $Size

    $hForm = GUICreate(StringFormat('%s - %.1f', $sFont, $nFont), 0, 0)
    GUISetFont(9.0, 400, 0, 'Segoe UI')
    GUISetBkColor(0xF0F0F0)
    GUICtrlCreateLabel('', 10, 10)
    GUICtrlSetFont(-1, $nFont, 400, 0, $sFont)
    GUICtrlSetBkColor(-1, 0xFFFFFF)
    $Size = _GetTabbedStringSizeEx(-1, $sText, 1)
    If Not @Error Then
        GUICtrlSetPos(-1, -1, -1, $Size[0], $Size[1])
        GUICtrlSetData(-1, $sText)
    EndIf
    $Pos = WinGetPos($hForm)
    WinMove($hForm, '', (@DesktopWidth - ($Pos[2] + $Size[0] + 20)) / 2, (@DesktopHeight - ($Pos[3] + $Size[1] + 70)) / 2, $Pos[2] + $Size[0] + 20, $Pos[3] + $Size[1] + 70)
    GUICtrlCreateGraphic(0, 0, $Size[0] + 20, $Size[1] + 20)
    GUICtrlSetBkColor(-1, 0xFFFFFF)
    GUICtrlSetState(-1, $GUI_DISABLE)
    GUICtrlCreateGraphic(0, $Size[1] + 20, $Size[0] + 20, 1)
    GUICtrlSetBkColor(-1, 0xDFDFDF)
    GUICtrlSetState(-1, $GUI_DISABLE)
    $Button = GUICtrlCreateButton('OK', ($Size[0] + 20) / 2 - 37, $Size[1] + 32, 88, 27)
    GUICtrlSetState(-1, BitOR($GUI_DEFBUTTON, $GUI_FOCUS))
    GUISetState()

    MouseMove(($Size[0] + 20) / 2 + 7, $Size[1] + 46, 0)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                Exit
            Case $Button
                ExitLoop
        EndSwitch
    WEnd

    GUIDelete($hForm)

EndFunc   ;==>_Form

Func _GetTabbedStringSizeEx($CtrlID, ByRef $sString, $fAlignment = False)

    Local $W = 0, $H, $tTM, $pTM, $hDC = 0, $hSv = 0, $aTemp, $aSize, $aData, $aTabs, $aText, $sText = '', $Error, $Tab, $Ret
    Local $tTM = DllStructCreate('long;long;long;long;long;long;long;long;long;long;long;wchar;wchar;wchar;wchar;byte;byte;byte;byte;byte')
;~  Local $tTM = DllStructCreate($tagTEXTMETRIC)
    Local $pTM = DllStructGetPtr($tTM)
    Local $tData = DllStructCreate('long;long')
    Local $pData = DllStructGetPtr($tData)
    Local $aResult[2] = [0, 0]

    If Not IsHWnd($CtrlID) Then
        $CtrlID = GUICtrlGetHandle($CtrlID)
        If Not $CtrlID Then
            Return SetError(1, 0, 0)
        EndIf
    EndIf
    Do
        $Error = 1
        $Ret = DllCall('user32.dll', 'ptr', 'GetDC', 'hwnd', $CtrlID)
        If (@Error) Or (Not $Ret[0]) Then
            ExitLoop
        EndIf
        $hDC = $Ret[0]
        $Ret = DllCall('user32.dll', 'ptr', 'SendMessageW', 'hwnd', $CtrlID, 'uint', 0x0031, 'wparam', 0, 'lparam', 0)
        If (@Error) Or (Not $Ret[0]) Then
            ExitLoop
        EndIf
        $Ret = DllCall('gdi32.dll', 'ptr', 'SelectObject', 'hwnd', $hDC, 'ptr', $Ret[0])
        If (@Error) Or (Not $Ret[0]) Then
            ExitLoop
        EndIf
        $hSv = $Ret[0]
        $Ret = DllCall('gdi32.dll', 'int', 'GetTextMetricsW', 'hwnd', $hDC, 'ptr', $pTM)
        If (@Error) Or (Not $Ret[0]) Then
            ExitLoop
        EndIf
        $Tab = 8 * DllStructGetData($tTM, 6)
        If Not $Tab Then
            ExitLoop
        EndIf
        $aData = StringSplit(StringReplace($sString, @CRLF, @CR), @CR, 2)
        $H = UBound($aData)
        If ($H > 1) And ($fAlignment) Then
            For $i = 0 To $H - 1
                $aTemp = StringSplit($aData[$i], @TAB)
                If $W < $aTemp[0] Then
                    $W = $aTemp[0]
                EndIf
            Next
            Dim $aText[$H][$W]
            Dim $aSize[$H][$W]
            For $i = 0 To $H - 1
                $aTemp = StringSplit($aData[$i], @TAB)
                For $j = 0 To $W - 1
                    Select
                        Case $aTemp[0] < $j + 1
                            $aText[$i][$j] = 0
                            $aSize[$i][$j] = -1
                        Case $aTemp[0] = $j + 1
                            $aText[$i][$j] = $aTemp[$j + 1]
                            $aSize[$i][$j] = -1
                        Case Else
                            $aText[$i][$j] = $aTemp[$j + 1]
                            $Ret = DllCall('gdi32.dll', 'int', 'GetTextExtentPoint32W', 'hwnd', $hDC, 'wstr', $aTemp[$j + 1], 'int', StringLen($aTemp[$j + 1]), 'ptr', $pData)
                            If (Not @Error) And ($Ret[0]) Then
                                $aSize[$i][$j] = DllStructGetData($tData, 1)
                            Else
                                $aSize[$i][$j] = 0
                            EndIf
                    EndSelect
                Next
            Next
            Dim $aTabs[$W]
            For $j = 0 To $W - 1
                $aTabs[$j] = 1
                For $i = 0 To $H - 1
                    If $aSize[$i][$j] <> -1 Then
                        If $aSize[$i][$j] < $Tab Then
                            $aSize[$i][$j] = 1
                        Else
                            $aSize[$i][$j] = Floor($aSize[$i][$j] / $Tab) + 1
                            If $aTabs[$j] < $aSize[$i][$j] Then
                                $aTabs[$j] = $aSize[$i][$j]
                            EndIf
                        EndIf
                    EndIf
                Next
            Next
            Dim $aData[$H]
            For $i = 0 To $H - 1
                $aData[$i] = ''
                For $j = 0 To $W - 1
                    If IsString($aText[$i][$j]) Then
                        $aData[$i] &= $aText[$i][$j]
                        If $aSize[$i][$j] <> -1 Then
                            $aSize[$i][$j] = $aTabs[$j] - $aSize[$i][$j]
                        EndIf
                        For $k = 1 To $aSize[$i][$j] + 1
                            $aData[$i] &= @TAB
                        Next
                    Else
                        ExitLoop
                    EndIf
                Next
                $sText &= $aData[$i] & @CRLF
            Next
        EndIf
        $Error = 1
        DllStructSetData($tData, 1, $Tab)
        For $i = 0 To $H - 1
            $Ret = DllCall('user32.dll', 'dword', 'GetTabbedTextExtentW', 'hwnd', $hDC, 'wstr', $aData[$i], 'int', StringLen($aData[$i]), 'int', 1, 'ptr', $pData)
            If (Not @Error) And ($Ret[0]) Then
                $aResult[1] += BitShift($Ret[0], 16)
                $Ret = BitAND($Ret[0], 0xFFFF)
                If $aResult[0] < $Ret Then
                    $aResult[0] = $Ret
                EndIf
            Else
                ExitLoop 2
            EndIf
        Next
        $Error = 0
    Until 1
    If $hSv Then
        DllCall('gdi32.dll', 'ptr', 'SelectObject', 'hwnd', $hDC, 'ptr', $hSv)
    EndIf
    If $hDC Then
        DllCall('user32.dll', 'int', 'ReleaseDC', 'hwnd', $CtrlID, 'hwnd', $hDC)
    EndIf
    If $Error Then
        Return SetError(1, 0, 0)
    EndIf
    If $fAlignment Then
        $sString = StringTrimRight($sText, 2)
    EndIf
    Return $aResult
EndFunc   ;==>_GetTabbedStringSizeEx
Edited by Yashied
Link to comment
Share on other sites

I slightly modified the function. It seems now a universal tool to retrieve dimensions of a string in AutoIt.

 

Global $Text[3] = ['Simple string', 'String' & @TAB & 'with' & @TAB & 'tab' & @TAB & 'characters', 'String with carriage' & @CRLF & 'return']
Global $Label[3]

$hForm = GUICreate('MyGUI', 318, 116)
GUISetFont(15, 400, 0, 'Tahoma')
For $i = 0 To 2
    $Label[$i] = GUICtrlCreateLabel($Text[$i], 0, 0)
    $Size = _GetTabbedStringSizeEx(-1, $Text[$i])
    GUICtrlSetPos(-1, 5, 5 + 29 * $i, $Size[0], $Size[1])
    GUICtrlSetBkColor(-1, 0xFFC0C0)
Next
GUISetState()

Do
Until GUIGetMsg() = -3

post_img_093.png

Edited by Yashied
Link to comment
Share on other sites

Any thoughts.

What is about solution of the inverse task of your one?

Receive size and font to place string given into label (edit) space with prescribed length and height.

However this task can have the set of fonts (with it's sizes). There is some of interest to get optimum from list of fonts.

Thank you.

The point of world view

Link to comment
Share on other sites

  • Moderators

Yashied,

Sorry I missed this when you posted it. Excellent work - probably the last word in measuring strings. ;)

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to comment
Share on other sites

  • 6 months later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...