I am making an app that will be showing quotes, and I need them to be in text (no pics). It took me a couple of hours so maybe it will save someone else's.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | function write_text(txt, txt_size, dim) --writte text into a rectangle of dimensions dim --dim is table with coordinates {x1,x2,y1,y2} --FYI: iPhone screen dimensions are 320x480 if txt_size == nil then txt_size = 12 --default text size if not set end if dim == nil then dim = {10,310,10,470} --default dimensions if dim is not set end local w = dim[2] - dim [1] local h = dim[4] - dim [3] print(h .. w) --draw a rectanle to see the borders clearly whiteRect = display.newRect( dim[1], dim[3], w, h ) whiteRect:setFillColor( 255,255,255 ) local t = display.newText( "", dim[1], dim[3], nil, txt_size ); --write text line by line, actually word by word.. :( --I will use this lmitation to my advantage and make a cool letter-for-letter animation while I'm at it :) local newlined_string = "" --here is variable for saved (finall) string with newlines and fitted words local next_word = "" --next_word is buffer for next whole word (until SPACE or endOfString is reached), is independant of lines local skip_space = false -- get 1 character, add it to string, and render it for i = 1, #txt do local c = txt:sub(i,i) --c is current character we are working with local test_string = newlined_string .. next_word .. c --string for testing rendered width (including half of word for exmple) -- local t = display.newText( test_string, dim[1], dim[3], nil, txt_size ); --t:setTextColor( 100, 100, 136, 255 ); --uncomment this line to see actuall rendering --see if rendering this character crossed our boundary if t.stageWidth >= w then --got outside boundary! newlined_string = newlined_string .. "\n" --insert newline (\n) skip_space = true --remember new line so next word (line of text) doesn't start with SPACE end if txt:sub(i+1,i+1) == " " or i== #txt then --if next character is 'SPACE' or we reached end of string, add word to strng if skip_space then --new line so no need for SPACE on beginning of that line skip_space = false next_word = next_word:sub(2, #next_word) --remove the first char (SPACE) from word end newlined_string = newlined_string .. next_word .. c --add last word + last char to string and then, next_word = "" --reset variable else --all other characters next_word = next_word .. c --push inside end end t = display.newText( newlined_string, dim[1], dim[3], nil, txt_size ); --finall text object t:setTextColor( 100, 100, 136, 255 ); --set text color here end local text = "Now while I;m making this thing I had an idea. I'll make a function/animation that will show letter-for-letter my text on screen. That sounds like fun." --USAGE: --write_text(text) --write_text(text, 20) write_text(text, 15, {100,263,150,300}) |
i need the same functionality in the app i'm working on so i'm eager to see what you come up with...thanks for sharing! anyone know if support for different fonts is coming?
OK! I've got something that seems to work pretty well. There's two functions. The first is called "wrap" and it's a helper function that adds line breaks to your text. The second is called wrappedText and it takes the text outputted from the helper function and displays everyline as text objects. It works in the simulator and on the device.
Use the second function, wrappedText, in your code. It accepts a string, character limit for wrapping, font size, font color, and indents. It outputs an group object that contains your text and that can be manipulated like any other object on the screen , e.g. myTextObject.x = 100.
One thing to note is that the API function display.newText() seems to choke on bad characters which are usually invisible. Your text must be clean. I had lots of trouble with invisible characters at the beginning of new lines in a file generated by a server-side script.
Enjoy!
GG
---8<---------------------------
-- Wrap text
function wrap(str, limit, indent, indent1)
indent = indent or ""
indent1 = indent1 or indent
limit = limit or 72
local here = 1-#indent1
return indent1..str:gsub("(%s+)()(%S+)()",
function(sp, st, word, fi)
if fi-here > limit then
here = st - #indent
return "\n"..indent..word
end
end)
end
function wrappedText(str, limit, size, color, indent, indent1)
--apply line breaks using the wrapping function
str = wrap(str, limit, indent, indent1)
size = size or 12
color = color or {255, 255, 255}
--search for each line that ends with a line break and add to an array
local pos, arr = 0, {}
for st,sp in function() return string.find(str,"\n",pos,true) end do
table.insert(arr,string.sub(str,pos,st-1))
pos = sp + 1
end
table.insert(arr,string.sub(str,pos))
--iterate through the array and add each item as a display object to the group
local g = display.newGroup()
local i = 1
while i <= #arr do
local t = display.newText( arr[i], 0, 0, 0, size )
t:setTextColor( color[1], color[2], color[3] )
t.xReference = -t.width/2
t.x = 0
t.yReference = -t.height/2
t.y = 14*(i-1)
g:insert(t)
i = i + 1
end
return g
end
---8<---------------------------
-- USAGE:
local myText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc euismod justo sapien, at sollicitudin lacus. Quisque vestibulum commodo felis id posuere."
local myTextObject = wrappedText(myText, 46)
--local myTextObject = wrappedText(myText, 46, 16)
--local myTextObject = wrappedText(myText, 46, 16, {255, 0, 0})
local myGroup = display.newGroup()
myGroup:insert( myTextObject )
hey thanks, much appreciated! by the way, i'm using some different size fonts and i noticed the vertical spacing was static...but i got some good results easily enough by changing this line:
t.y = 14*(i-1)
to this:
t.y = size*(i-1)
thanks, you just saved me a chunk of time...i'm going to find something in my code now to share with the world
Thanks, ruuzo. I just added your code with a modification for improved readability. I'm using size*1.3 rather than just size. Try it. It's much better and more readable.
I also made an update to support paragraph spaces better. The wrap() helper function was including line breaks such as "\n" in its character count, which made the beginning lines of new paragraphs too short. With the addition of another helper function, explode(), that is not longer a problem. Here's the new code:
----8<------------------------
-- Wrap text
<code>function wrap(str, limit, indent, indent1)
indent = indent or ""
indent1 = indent1 or indent
limit = limit or 72
local here = 1-#indent1
return indent1..str:gsub("(%s+)()(%S+)()",
function(sp, st, word, fi)
if fi-here > limit then
here = st - #indent
return "\n"..indent..word
end
end)
end
function explode(div,str)
if (div=='') then return false end
local pos,arr = 0,{}
-- for each divider found
for st,sp in function() return string.find(str,div,pos,true) end do
table.insert(arr,string.sub(str,pos,st-1)) -- Attach chars left of current divider
pos = sp + 1 -- Jump past current divider
end
table.insert(arr,string.sub(str,pos)) -- Attach chars right of last divider
return arr
end
function wrappedText(str, limit, size, color, indent, indent1)
str = explode("\n", str)
--apply line breaks using the wrapping function
local i = 1
local strFinal = ""
while i <= #str do
strW = wrap(str[i], limit, indent, indent1)
size = size or 12
color = color or {255, 255, 255}
strFinal = strFinal.."\n"..strW
i = i + 1
end
str = strFinal
--search for each line that ends with a line break and add to an array
local pos, arr = 0, {}
for st,sp in function() return string.find(str,"\n",pos,true) end do
table.insert(arr,string.sub(str,pos,st-1))
pos = sp + 1
end
table.insert(arr,string.sub(str,pos))
--iterate through the array and add each item as a display object to the group
local g = display.newGroup()
local i = 1
while i <= #arr do
local t = display.newText( arr[i], 0, 0, 0, size )
t:setTextColor( color[1], color[2], color[3] )
t.xReference = -t.width/2
t.x = 0
t.yReference = -t.height/2
t.y = (size*1.3)*(i-1)
g:insert(t)
i = i + 1
end
return g
end
----8<------------------------
-- USAGE:
local myText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc euismod justo sapien, at sollicitudin lacus. Quisque vestibulum commodo felis id posuere."
local myTextObject = wrappedText(myText, 46)
--local myTextObject = wrappedText(myText, 46, 16)
--local myTextObject = wrappedText(myText, 46, 16, {255, 0, 0})
local myGroup = display.newGroup()
myGroup:insert( myTextObject )
By the way, this function works best with line breaks that are all \n . So be sure to replace any \r with \n before sending your text to this function . For example, in PHP use $str = str_replace("\r", "\n", $str)
Cheers,
GG
Actually I haven't tried it on the iPhone, I'm still waiting for my activation code.
Hopefully next update will solve this issue so we don't have to use these workarounds. Until then, it's good that you found a way to make it work.
@Ansca people: I hope we get text tools available as soon as possible (including text input - keyboard)!
I promise, its a top priority. ;)
I've added support for fonts and text color when you're wrapping text. Here's the new code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | ---8<----------------------------------------- -- Wrap text function wrap(str, limit, indent, indent1) indent = indent or "" indent1 = indent1 or indent limit = limit or 72 local here = 1-#indent1 return indent1..str:gsub("(%s+)()(%S+)()", function(sp, st, word, fi) if fi-here > limit then here = st - #indent return "\n"..indent..word end end) end function explode(div,str) if (div=='') then return false end local pos,arr = 0,{} -- for each divider found for st,sp in function() return string.find(str,div,pos,true) end do table.insert(arr,string.sub(str,pos,st-1)) -- Attach chars left of current divider pos = sp + 1 -- Jump past current divider end table.insert(arr,string.sub(str,pos)) -- Attach chars right of last divider return arr end function wrappedText(str, limit, size, font, color, indent, indent1) str = explode("\n", str) size = tonumber(size) or 12 color = color or {255, 255, 255} font = font or "Helvetica" --apply line breaks using the wrapping function local i = 1 local strFinal = "" while i <= #str do strW = wrap(str[i], limit, indent, indent1) strFinal = strFinal.."\n"..strW i = i + 1 end str = strFinal --search for each line that ends with a line break and add to an array local pos, arr = 0, {} for st,sp in function() return string.find(str,"\n",pos,true) end do table.insert(arr,string.sub(str,pos,st-1)) pos = sp + 1 end table.insert(arr,string.sub(str,pos)) --iterate through the array and add each item as a display object to the group local g = display.newGroup() local i = 1 while i <= #arr do local t = display.newText( arr[i], 0, 0, font, size ) t:setTextColor( color[1], color[2], color[3] ) t.x = math.floor(t.width/2) t.y = (size*1.3)*(i-1) g:insert(t) i = i + 1 end return g end ---8<----------------------------------------- -- USAGE: local myText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc euismod justo sapien, at sollicitudin lacus. Quisque vestibulum commodo felis id posuere." local myTextObject = wrappedText( myText, 46, 16, "Helvetica", {255, 0, 0} ) local myGroup = display.newGroup() myGroup:insert( myTextObject ) |
Gilbert
thanks for your code. Great.
As what I could test, it only supports one orientation. If you rotate it, it disapears.
A good improvement is the ability to rotate the text box
thks
j
@Gilbert - Great! That is exactly what I wanted. Thanks so much.
Hi,
Have you tried your code on a device? I tried it on my iPhone and it just shows a band of color.
I have some Lua code that does something similar, and it works great in the simulator, but just shows a stripe of color instead of the text. Here's the code:
function wrap(str, limit, indent, indent1)
indent = indent or ""
indent1 = indent1 or indent
limit = limit or 72
local here = 1-#indent1
return indent1..str:gsub("(%s+)()(%S+)()",
function(sp, st, word, fi)
if fi-here > limit then
here = st - #indent
return "\n"..indent..word
end
end)
end
I posted a bug about this here: https://developer.anscamobile.com/forum/2009/12/29/text-wraps-inconsistently-simulator-and-device
Apparently, line breaks work in the simulator but not on the device yet.
I'm working on a new text wrapping function that just writes each line as a new text display object. I'll post it soon.
Cheers,
GG