The function getLineStrings
will give you a table that contains text for each line. The number of lines is the number of values in the table returned by the function. You can use the #
operator to get that number. I put some information as comments to the code. I’m not 100% sure if everything works properly, but based on my tests, it seems to work.
Explanation of how the function works:
For each line the function does this. First, it gets a substring of the whole string. This substring starts from the character that comes after the character at the end of the latest line. The number of characters in this substring is the calculated average character number per line. Then it modifies this substring so that fits in the given X size as well as possible. It makes it shorter, if necessary, or longer, if possible.
After that it uses the cutLine
function above it. CutLine makes sure that the line will have a proper end. For example, it makes sure that if there’s a word like ‘result’, in the whole string and the main function cuts it so that there’s ‘res’ in the end of the line, cutLine
will change this to ‘re-’ . Then there will be ‘sult’ at the start of the next line. If it can’t find a proper way to cut a word, it’ll remove the word completely from that line and the word will be in the next line.
After using the cutLine
function once, the main functions checks that there isn’t a whitespace character starting the next line. Tab is an exeption, it accepts that, but if there’s some other whitespace character, then the function will use the cutLine
function again. I did this so that lines won’t begin weirdly because of there being a space as the first character. The tab exeption might not really make sense, though because you probably wouldn’t use it anyway, so maybe it wasn’t a very good addition.
After doing all this, the main function continues to the next line.
local TextService = game:GetService("TextService")
-- math.huge so that these values won't have effect on the value given by getTextSize
local frameSize = Vector2.new(math.huge, math.huge)
local vowels = {"a", "e", "i", "o", "u", "y", "å", "ä", "ö"}
local consonants = {"b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "z"}
local function cutLine(s, lastCharNum)
if s:sub(1, 1):match("%s") then
warn("line starts with whitespace!")
end
local returnString, returnLastCharNum
local addition = false -- tells if a - was added at the end of the word
local rev = s:reverse()
local revLen = rev:len()
if rev:sub(1, 1) == " " then
returnString = s
else local whitespaceBeforeLastWordPos = rev:find("%s")
local sWithoutLastWord = whitespaceBeforeLastWordPos and rev:sub(whitespaceBeforeLastWordPos, revLen):reverse() or ""
local wordToCut = rev:sub(1, whitespaceBeforeLastWordPos and whitespaceBeforeLastWordPos-1 or revLen)
local wordToCutLen = wordToCut:len()
for i = 1, wordToCutLen do
if table.find(consonants, wordToCut:sub(i, i):lower()) then
local nextChar, nextNextChar = wordToCut:sub(i+1, i+1):lower(), wordToCut:sub(i+2, i+2):lower()
local nextCharConsonant = table.find(consonants, nextChar) ~= nil
if i == wordToCutLen or (i+1 == wordToCutLen and nextCharConsonant) then
returnString = sWithoutLastWord
break
end
if ((nextCharConsonant and table.find(vowels, nextNextChar)) or table.find(vowels, nextChar)) and table.find(vowels, wordToCut:sub(i-1, i-1)) then
returnString = sWithoutLastWord..wordToCut:sub(i+1, wordToCutLen):reverse().."-"
addition = true
break
end
end
end
if not returnString then
returnString = sWithoutLastWord
end
if returnString == "" then
returnString = s
end
end
returnLastCharNum = lastCharNum-s:len()+returnString:len()
if addition then
returnLastCharNum -= 1 -- so that adding the - won't affect the behavior of the main function
end
return returnString, returnLastCharNum
end
-- parameters: the whole text, fontsize as a number, font as an enum, labelsize in pixels as a vector2 (TextLabel.AbsoluteSize)
-- the Y size isn't actually even used, so this function could be changed so that it just takes the x value as a number.
local function getLineStrings(wholeString, fontSize, font, labelSize)
-- the text of each line is stored here. to get the number of lines you can do #lines
local lines = {}
local mainLoopRepeats = 0
-- length of the whole string in pixels
local wholeStringXLength = TextService:GetTextSize(wholeString, fontSize, font, frameSize).X
-- number of lines the whole string will probably take
local numberOfLines = wholeStringXLength/labelSize.X
-- number of characters in the whole string
local stringLen = wholeString:len()
-- how many characters on the average there will be per line
local charsPerLine = math.ceil(stringLen/numberOfLines)
local latestLineLastCharNum = 0
while latestLineLastCharNum < stringLen do
if mainLoopRepeats == 25 then
break
end
local currentFirstCharNum = latestLineLastCharNum+1
local lastCharNum = math.clamp(latestLineLastCharNum+charsPerLine, 1, stringLen)
local s = wholeString:sub(currentFirstCharNum, lastCharNum)
local sSizeX = TextService:GetTextSize(s, fontSize, font, frameSize).X
if sSizeX > labelSize.X then
while TextService:GetTextSize(s, fontSize, font, frameSize).X > labelSize.X do
lastCharNum -= 1
s = s:sub(1, s:len()-1)
end
elseif lastCharNum < stringLen then
s = wholeString:sub(currentFirstCharNum, lastCharNum+1)
while TextService:GetTextSize(s, fontSize, font, frameSize).X < labelSize.X do
lastCharNum += 1
s = wholeString:sub(currentFirstCharNum, lastCharNum+1)
if lastCharNum == stringLen then
s = s.."_" -- a placeholder so that nothing important will be removed after this loop
break
end
end
s = s:sub(1, s:len()-1)
end
if lastCharNum < stringLen then
s, lastCharNum = cutLine(s, lastCharNum)
end
local sRev, sLen = s:reverse(), s:len()
local whiteSpaceOnNextLineStart = wholeString:sub(lastCharNum+1, lastCharNum+1):match("%s")
if whiteSpaceOnNextLineStart then
local nonWhiteSpaceStart = sRev:find("%S")
-- if there is something else than whitespace in somewhere in the current line and the whitespace in the beginning of the next line is't tab
if nonWhiteSpaceStart and whiteSpaceOnNextLineStart ~= "\t" then
-- preventing the next line from starting with whitespace
s, lastCharNum = cutLine(s:sub(1, sLen-nonWhiteSpaceStart+1), lastCharNum-nonWhiteSpaceStart+1)
end
end
latestLineLastCharNum = lastCharNum
mainLoopRepeats += 1
lines[mainLoopRepeats] = s
end
return lines
end