Splitting text into different pages

I am trying to create a book system. I am finding a way to split the text of the book into multiple pages. The book consists of multiple TextLabels with a fixed size written in a fixed font size.

What I have so far doesn’t fully split the text, it leaves behind a few words.

local TextService = game:GetService("TextService")
local current_text = {}

local function split_text(text: string): ()
	local split_by_space = string.split(text, " ")

	for index = 1, #split_by_space do
		local substring = table.concat(split_by_space, " ", 1, index)
		local text_size = TextService:GetTextSize(
			substring, 
			50,
			Enum.Font.Fondamento,
			Vector2.new(338, 600)
		)
		
		if text_size.Y >= 600 then
			table.insert(current_text, table.concat(split_by_space, " ", 1, index - 1))

			local remaining_text = {}
			table.move(split_by_space, index, #split_by_space, 1, remaining_text)
			split_text(table.concat(remaining_text, " "))
			break
		end
	end
end

split_text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
print(current_text)

The output is:
{
[1] = “Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea”,
[2] = “commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim”
}
which does not include the last 3 words.

Hello mekeldev, I believe the fix would be simply by inserting a CanSurpass check, if the full textSize.Y is > 600 then it’ll follow the same process to split the text, otherwhise it’ll create a new page with the rest words. Hopefully the script below can help with your problem :smile:

local TextService = game:GetService("TextService")
local current_text = {}

local function split_text(text: string): ()
	local split_by_space = string.split(text, " ")

	local function canSurprass()
		-- Checks if the full textSize.Y surpass the 600
		-- if yes, the script runs like the first one you've done
		-- if no, the text will be considered a rest and will be inserted on the reamining texts and a new page will be created
		local subString = table.concat(split_by_space, " ", 1, #split_by_space)
		local textSize = TextService:GetTextSize(
			subString,
			50,
			Enum.Font.Fondamento,
			Vector2.new(338, 600)
		)

		if textSize.Y >= 600 then
			return true
		else
			return false
		end
	end

	for index = 1, #split_by_space do
		if canSurprass() then 
			local substring = table.concat(split_by_space, " ", 1, index)
			local text_size = TextService:GetTextSize(
				substring, 
				50,
				Enum.Font.Fondamento,
				Vector2.new(338, 600)
			)

			if text_size.Y >= 600 then
				table.insert(current_text, table.concat(split_by_space, " ", 1, index - 1))

				local remaining_text = {}
				table.move(split_by_space, index, #split_by_space, 1, remaining_text)
				split_text(table.concat(remaining_text, " "))
				break
			end
		else
			local remainingText = {}
			table.insert(remainingText, table.concat(split_by_space, " ", 1, index))
			table.insert(current_text, table.concat(split_by_space, " "))
			break
		end
	end
end

split_text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
print(current_text)

Thanks for the solution!

For anyone else that needs it, I refactored some of it and here’s the final working code:

local TextService = game:GetService("TextService")
local current_text = {}

local function text_to_page(text: string): ()
	local split_text = string.split(text, " ")

	for index = 1, #split_text do
		if index == #split_text then
			table.insert(current_text, text)
			return
		end

		local temporary_substring = table.concat(split_text, " ", 1, index)
		local text_size = TextService:GetTextSize(
			temporary_substring, 50, Enum.Font.Fondamento, Vector2.new(338, 600)
		)

		if text_size.Y >= 600 then
			local valid_substring = table.concat(split_text, " ", 1, index - 1)
			table.insert(current_text, valid_substring)

			for new_index = 1, index - 1, 1 do
				split_text[new_index] = nil
			end

			local new_string = {}
			for _, value in split_text do
				table.insert(new_string, value)
			end

			text_to_page(table.concat(new_string, " "))
			return
		end
	end
end