Tyleo-Dev-AutoIT-Oldies/MSI AutoInstaller/MetroGUI-UDF/MetroUDF-Required/StringSize.au3
2024-02-18 22:17:39 +01:00

279 lines
14 KiB
AutoIt
Executable file

#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, "((?<!\x0d)\x0a|\x0d(?!\x0a))", @CRLF)
Local $asLines = StringSplit($sText, @CRLF, 1)
; For each line
For $i = 1 To $asLines[0]
; Expand tabs if required
If $iExpTab Then
$asLines[$i] = StringReplace($asLines[$i], @TAB, " XXXXXXXX")
EndIf
; Size line
$iLine_Length = StringLen($asLines[$i])
DllCall("gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $asLines[$i], "int", $iLine_Length, "ptr", DllStructGetPtr($tSize))
If @error Then Return SetError(2, _StringSize_Error_Close(6, $hDC, $hFont, $hLabel), 0)
If DllStructGetData($tSize, "X") > $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