#include-once ; #INDEX# ============================================================================================================ ; Title .........: _StringSize ; AutoIt Version : v3.2.12.1 or higher ; Language ......: English ; Description ...: Returns size of rectangle required to display string - maximum width can be chosen ; Remarks .......: ; Note ..........: ; Author(s) .....: Melba23 - thanks to trancexx for the default DC code ; ==================================================================================================================== ;#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 ; #CURRENT# ========================================================================================================== ; _StringSize: Returns size of rectangle required to display string - maximum width can be chosen ; ==================================================================================================================== ; #INTERNAL_USE_ONLY#================================================================================================= ; _StringSize_Error_Close: Releases DC and deletes font object after error ; _StringSize_DefaultFontName: Determines Windows default font ; ==================================================================================================================== ; #FUNCTION# ========================================================================================================= ; Name...........: _StringSize ; Description ...: Returns size of rectangle required to display string - maximum permitted width can be chosen ; Syntax ........: _StringSize($sText[, $iSize[, $iWeight[, $iAttrib[, $sName[, $iWidth[, $hWnd]]]]]]) ; Parameters ....: $sText - String to display ; $iSize - [optional] Font size in points - (default = 8.5) ; $iWeight - [optional] Font weight - (default = 400 = normal) ; $iAttrib - [optional] Font attribute (0-Normal (default), 2-Italic, 4-Underline, 8 Strike) ; + 1 if tabs are to be expanded before sizing ; $sName - [optional] Font name - (default = Tahoma) ; $iWidth - [optional] Max width for rectangle - (default = 0 => width of original string) ; $hWnd - [optional] GUI in which string will be displayed - (default 0 => normally not required) ; Requirement(s) : v3.2.12.1 or higher ; Return values .: Success - Returns 4-element array: ($iWidth set // $iWidth not set) ; |$array[0] = String reformatted with additonal @CRLF // Original string ; |$array[1] = Height of single line in selected font // idem ; |$array[2] = Width of rectangle required for reformatted // original string ; |$array[3] = Height of rectangle required for reformatted // original string ; Failure - Returns 0 and sets @error: ; |1 - Incorrect parameter type (@extended = parameter index) ; |2 - DLL call error - extended set as follows: ; |1 - GetDC failure ; |2 - SendMessage failure ; |3 - GetDeviceCaps failure ; |4 - CreateFont failure ; |5 - SelectObject failure ; |6 - GetTextExtentPoint32 failure ; |3 - Font too large for chosen max width - a word will not fit ; Author ........: Melba23 - thanks to trancexx for the default DC code ; Modified ......: ; Remarks .......: The use of the $hWnd parameter is not normally necessary - it is only required if the UDF does not ; return correct dimensions without it. ; Related .......: ; Link ..........: ; Example .......: Yes ;===================================================================================================================== Func _StringSize($sText, $iSize = 8.5, $iWeight = 400, $iAttrib = 0, $sName = "", $iMaxWidth = 0, $hWnd = 0) ; Set parameters passed as Default If $iSize = Default Then $iSize = 8.5 If $iWeight = Default Then $iWeight = 400 If $iAttrib = Default Then $iAttrib = 0 If $sName = "" Or $sName = Default Then $sName = _StringSize_DefaultFontName() ; Check parameters are correct type If Not IsString($sText) Then Return SetError(1, 1, 0) If Not IsNumber($iSize) Then Return SetError(1, 2, 0) If Not IsInt($iWeight) Then Return SetError(1, 3, 0) If Not IsInt($iAttrib) Then Return SetError(1, 4, 0) If Not IsString($sName) Then Return SetError(1, 5, 0) If Not IsNumber($iMaxWidth) Then Return SetError(1, 6, 0) If Not IsHwnd($hWnd) And $hWnd <> 0 Then Return SetError(1, 7, 0) Local $aRet, $hDC, $hFont, $hLabel = 0, $hLabel_Handle ; Check for tab expansion flag Local $iExpTab = BitAnd($iAttrib, 1) ; Remove possible tab expansion flag from font attribute value $iAttrib = BitAnd($iAttrib, BitNot(1)) ; If GUI handle was passed If IsHWnd($hWnd) Then ; Create label outside GUI borders $hLabel = GUICtrlCreateLabel("", -10, -10, 10, 10) $hLabel_Handle = GUICtrlGetHandle(-1) GUICtrlSetFont(-1, $iSize, $iWeight, $iAttrib, $sName) ; Create DC $aRet = DllCall("user32.dll", "handle", "GetDC", "hwnd", $hLabel_Handle) If @error Or $aRet[0] = 0 Then GUICtrlDelete($hLabel) Return SetError(2, 1, 0) EndIf $hDC = $aRet[0] $aRet = DllCall("user32.dll", "lparam", "SendMessage", "hwnd", $hLabel_Handle, "int", 0x0031, "wparam", 0, "lparam", 0) ; $WM_GetFont If @error Or $aRet[0] = 0 Then GUICtrlDelete($hLabel) Return SetError(2, _StringSize_Error_Close(2, $hDC), 0) EndIf $hFont = $aRet[0] Else ; Get default DC $aRet = DllCall("user32.dll", "handle", "GetDC", "hwnd", $hWnd) If @error Or $aRet[0] = 0 Then Return SetError(2, 1, 0) $hDC = $aRet[0] ; Create required font $aRet = DllCall("gdi32.dll", "int", "GetDeviceCaps", "handle", $hDC, "int", 90) ; $LOGPIXELSY If @error Or $aRet[0] = 0 Then Return SetError(2, _StringSize_Error_Close(3, $hDC), 0) Local $iInfo = $aRet[0] $aRet = DllCall("gdi32.dll", "handle", "CreateFontW", "int", -$iInfo * $iSize / 72, "int", 0, "int", 0, "int", 0, _ "int", $iWeight, "dword", BitAND($iAttrib, 2), "dword", BitAND($iAttrib, 4), "dword", BitAND($iAttrib, 8), "dword", 0, "dword", 0, _ "dword", 0, "dword", 5, "dword", 0, "wstr", $sName) If @error Or $aRet[0] = 0 Then Return SetError(2, _StringSize_Error_Close(4, $hDC), 0) $hFont = $aRet[0] EndIf ; Select font and store previous font $aRet = DllCall("gdi32.dll", "handle", "SelectObject", "handle", $hDC, "handle", $hFont) If @error Or $aRet[0] = 0 Then Return SetError(2, _StringSize_Error_Close(5, $hDC, $hFont, $hLabel), 0) Local $hPrevFont = $aRet[0] ; Declare variables Local $avSize_Info[4], $iLine_Length, $iLine_Height = 0, $iLine_Count = 0, $iLine_Width = 0, $iWrap_Count, $iLast_Word, $sTest_Line ; Declare and fill Size structure Local $tSize = DllStructCreate("int X;int Y") DllStructSetData($tSize, "X", 0) DllStructSetData($tSize, "Y", 0) ; Ensure EoL is @CRLF and break text into lines $sText = StringRegExpReplace($sText, "((? $iLine_Width Then $iLine_Width = DllStructGetData($tSize, "X") If DllStructGetData($tSize, "Y") > $iLine_Height Then $iLine_Height = DllStructGetData($tSize, "Y") Next ; Check if $iMaxWidth has been both set and exceeded If $iMaxWidth <> 0 And $iLine_Width > $iMaxWidth Then ; Wrapping required ; For each Line For $j = 1 To $asLines[0] ; Size line unwrapped $iLine_Length = StringLen($asLines[$j]) DllCall("gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $asLines[$j], "int", $iLine_Length, "ptr", DllStructGetPtr($tSize)) If @error Then Return SetError(2, _StringSize_Error_Close(6, $hDC, $hFont, $hLabel), 0) ; Check wrap status If DllStructGetData($tSize, "X") < $iMaxWidth - 4 Then ; No wrap needed so count line and store $iLine_Count += 1 $avSize_Info[0] &= $asLines[$j] & @CRLF Else ; Wrap needed so zero counter for wrapped lines $iWrap_Count = 0 ; Build line to max width While 1 ; Zero line width $iLine_Width = 0 ; Initialise pointer for end of word $iLast_Word = 0 ; Add characters until EOL or maximum width reached For $i = 1 To StringLen($asLines[$j]) ; Is this just past a word ending? If StringMid($asLines[$j], $i, 1) = " " Then $iLast_Word = $i - 1 ; Increase line by one character $sTest_Line = StringMid($asLines[$j], 1, $i) ; Get line length $iLine_Length = StringLen($sTest_Line) DllCall("gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sTest_Line, "int", $iLine_Length, "ptr", DllStructGetPtr($tSize)) If @error Then Return SetError(2, _StringSize_Error_Close(6, $hDC, $hFont, $hLabel), 0) $iLine_Width = DllStructGetData($tSize, "X") ; If too long exit the loop If $iLine_Width >= $iMaxWidth - 4 Then ExitLoop Next ; End of the line of text? If $i > StringLen($asLines[$j]) Then ; Yes, so add final line to count $iWrap_Count += 1 ; Store line $avSize_Info[0] &= $sTest_Line & @CRLF ExitLoop Else ; No, but add line just completed to count $iWrap_Count += 1 ; Check at least 1 word completed or return error If $iLast_Word = 0 Then Return SetError(3, _StringSize_Error_Close(0, $hDC, $hFont, $hLabel), 0) ; Store line up to end of last word $avSize_Info[0] &= StringLeft($sTest_Line, $iLast_Word) & @CRLF ; Strip string to point reached $asLines[$j] = StringTrimLeft($asLines[$j], $iLast_Word) ; Trim leading whitespace $asLines[$j] = StringStripWS($asLines[$j], 1) ; Repeat with remaining characters in line EndIf WEnd ; Add the number of wrapped lines to the count $iLine_Count += $iWrap_Count EndIf Next ; Reset any tab expansions If $iExpTab Then $avSize_Info[0] = StringRegExpReplace($avSize_Info[0], "\x20?XXXXXXXX", @TAB) EndIf ; Complete return array $avSize_Info[1] = $iLine_Height $avSize_Info[2] = $iMaxWidth ; Convert lines to pixels and add drop margin $avSize_Info[3] = ($iLine_Count * $iLine_Height) + 4 Else ; No wrapping required ; Create return array (add drop margin to height) Local $avSize_Info[4] = [$sText, $iLine_Height, $iLine_Width, ($asLines[0] * $iLine_Height) + 4] EndIf ; Clear up DllCall("gdi32.dll", "handle", "SelectObject", "handle", $hDC, "handle", $hPrevFont) DllCall("gdi32.dll", "bool", "DeleteObject", "handle", $hFont) DllCall("user32.dll", "int", "ReleaseDC", "hwnd", 0, "handle", $hDC) If $hLabel Then GUICtrlDelete($hLabel) Return $avSize_Info EndFunc ;==>_StringSize ; #INTERNAL_USE_ONLY#============================================================================================================ ; Name...........: _StringSize_Error_Close ; Description ...: Releases DC and deleted font object if required after error ; Syntax ........: _StringSize_Error_Close ($iExtCode, $hDC, $hGUI) ; Parameters ....: $iExtCode - code to return ; $hDC, $hGUI - handles as set in _StringSize function ; Return value ..: $iExtCode as passed ; Author ........: Melba23 ; Modified.......: ; Remarks .......: This function is used internally by _StringSize ; =============================================================================================================================== Func _StringSize_Error_Close($iExtCode, $hDC = 0, $hFont = 0, $hLabel = 0) If $hFont <> 0 Then DllCall("gdi32.dll", "bool", "DeleteObject", "handle", $hFont) If $hDC <> 0 Then DllCall("user32.dll", "int", "ReleaseDC", "hwnd", 0, "handle", $hDC) If $hLabel Then GUICtrlDelete($hLabel) Return $iExtCode EndFunc ;=>_StringSize_Error_Close ; #INTERNAL_USE_ONLY#============================================================================================================ ; Name...........: _StringSize_DefaultFontName ; Description ...: Determines Windows default font ; Syntax ........: _StringSize_DefaultFontName() ; Parameters ....: None ; Return values .: Success - Returns name of system default font ; Failure - Returns "Tahoma" ; Author ........: Melba23, based on some original code by Larrydalooza ; Modified.......: ; Remarks .......: This function is used internally by _StringSize ; =============================================================================================================================== Func _StringSize_DefaultFontName() ; Get default system font data Local $tNONCLIENTMETRICS = DllStructCreate("uint;int;int;int;int;int;byte[60];int;int;byte[60];int;int;byte[60];byte[60];byte[60]") DLLStructSetData($tNONCLIENTMETRICS, 1, DllStructGetSize($tNONCLIENTMETRICS)) DLLCall("user32.dll", "int", "SystemParametersInfo", "int", 41, "int", DllStructGetSize($tNONCLIENTMETRICS), "ptr", DllStructGetPtr($tNONCLIENTMETRICS), "int", 0) Local $tLOGFONT = DllStructCreate("long;long;long;long;long;byte;byte;byte;byte;byte;byte;byte;byte;char[32]", DLLStructGetPtr($tNONCLIENTMETRICS, 13)) If IsString(DllStructGetData($tLOGFONT, 14)) Then Return DllStructGetData($tLOGFONT, 14) Else Return "Tahoma" EndIf EndFunc ;=>_StringSize_DefaultFontName