Ok, worked on another issue the last couple of days, seems like everything is working fine now.
Problem was that there are emojis that are a combination of emojis, like a the “family-emoji”, which consists of a man, a woman and a girl that are combined used a so called zero-width-joiner. In addition to that, there are fitzpatrick-modifier for different skin-colors, so that a normal “man”-emoji can become a “man-lightskin”, “man-mediumlightskin” and so on.
All these special cases should be handled now. I had to forget the idea of taking a snapshot after everything is rendered, because scrollViews with different texts, onscreen and below, dont allow taking snapshots of parts that are not yet visible.
This version does not yet consider different imageSheets for Android/iOS, but this souldnt be a problem to implement.
This is the final result:
-- =================================================================================== -- =================================================================================== -- This module loops through strings, extracts every word (separated by space or emoji) -- into a single display.newText() and every single emoji into a display.newImageRect() -- Words and emojis are aligned next to each other, taking into consideration -- newlines when maxwidth is reached. -- Combined emojis (e.g. "family: Men-woman-boy-girl") are handled. -- =============================================================================== -- =============================================================================== -- no emojis without utf8 local utf8 = require("plugin.utf8") -- create module-table that is returned at the end local emojiModule = {} -- create our emojiMap, gets filled at the bottom local emojiMap = {} -- setup spriteSheet with emojis local options = { width = 32, height = 32, numFrames = 43 ^ 2, sheetContentWidth = 1376, sheetContentHeight = 1376 } local sheet = graphics.newImageSheet( "emojis.png", options ) -- write down codepoints for modifiers local modFemale = 9792 -- uses female version of neutral emoji local modMale = 9794 -- uses male version of neutral emoji local modJoin = 8205 -- zero-width-joiner to combine emojis local modVariant = 65039 -- indicator that last object is an emoji local modLightSkin = 127995 -- fitzpatrick1: modify emoji with light skin local modMediumLightSkin = 127996 -- fitzpatrick2: modify emoji with medium light skin local modMediumSkin = 127997 -- fitzpatrick3: modify emoji with medium skin local modMediumDarkSkin = 127998 -- fitzpatrick4: modify emoji with medium dark skin local modDarkSkin = 127999 -- fitzpatrick5: modify emoji with dark skin -- function to create our emoijiText; -- parameters: -- (displaygroup to put elements in, inputstring, initial X, initial Y, max-width of line, font, fontSize, fontColor) function emojiModule.createEmojiText(group, text, x, y, width, font, fontSize, fillColor, filename) -- create a tempGroup local tempGroup = display.newGroup() group:insert(tempGroup) -- get font metrics since iOS and android treat metrics.leading different when rendering local metrics = graphics.getFontMetrics( font, fontSize ) local fontLeading = metrics.leading -- set initial x and y positions for first char or emoji local curX = x local curY = y -- set initial width to 0, adds up with each char or emoji local currentWidth = 0 -- create substring for chars, gets printed on screen once we see a space, an emoji or end of input-string local substring = "" -- create emoji substring (emojis can be combinations of emojis) local currentEmoji = "" -- create a flag indicating whether an emoji is a combination of emojis local combinedEmoji = false -- loop through input string for charpos, codepoint in utf8.codes(text) do -- ======================== -- if next object is a char -- ======================== if( codepoint \<= 255) then -- check if there is an unrendered emoji left if(currentEmoji ~= "") then -- if yes, create emoji local emoji -- normal case: We have the emoji in our Sheet if( emojiMap[currentEmoji] ) then emoji = display.newImageRect( tempGroup, sheet, emojiMap[currentEmoji], fontSize, fontSize) emoji.x = curX emoji.y = curY -- fallback: We dont have this emoji, use a standard one of your choice else emoji = display.newImageRect( tempGroup, sheet, emojiMap["128514"], fontSize, fontSize) emoji.x = curX emoji.y = curY end -- set anchorPoint emoji.anchorX = 0 -- update current Width currentWidth = currentWidth + emoji.contentWidth -- check if we passed max-width (end of line) if(currentWidth \> width) then -- if yes, reset positions for next char / emoji curX = x curY = curY + fontSize -- move our emoji into next line emoji.x = curX emoji.y = curY -- reset currentWidth currentWidth = emoji.contentWidth -- and y-translate group tempGroup.y = tempGroup.y - fontSize/2 end -- reset currentEmoji currentEmoji = "" combinedEmoji = false -- update currentX for next char/emoji curX = curX + emoji.contentWidth end -- add char to our substring substring = substring .. utf8.char(codepoint) -- in case char is a space if(codepoint == 32) then -- render substring that has been built so far -- has to be done since otherwise substring might get longer than max-width local obj = display.newText( tempGroup, substring, curX, curY, font, fontSize ) obj:setFillColor(fillColor) obj.anchorX = 0 -- on iOS, add font-leading to y-center text correct if (system.getInfo( "platform" ) == "ios") then obj.y = obj.y + fontLeading end -- update currentWidth currentWidth = currentWidth + obj.contentWidth -- check if we passed max-width (end of line) if(currentWidth \> width) then -- if yes, reset positions for next char / emoji curX = x curY = curY + fontSize -- reset currentWidth currentWidth = obj.contentWidth -- move our substring into next line obj.x = curX obj.y = curY -- and y-translate group group.y = group.y - fontSize/2 end -- reset substring substring = "" -- update currentX curX = curX + obj.contentWidth end -- ============================= -- else: next object is an emoji -- ============================= else -- render current substring, but only if it hasnt been rendered yet -- detected by checking currenct substring (is empty after a space) if(substring ~= "") then -- render substring that has been built so far local obj = display.newText( tempGroup, substring, curX, curY, font, fontSize ) obj:setFillColor(fillColor) obj.anchorX = 0 -- on iOS, add font-leading to y-center text correct if (system.getInfo( "platform" ) == "ios") then obj.y = obj.y + fontLeading end -- update currentWidth currentWidth = currentWidth + obj.contentWidth -- check if we passed of max-width (end of line) if(currentWidth \> width) then -- if yes, reset positions for next char / emoji curX = x curY = curY + fontSize -- reset currentWidth currentWidth = obj.contentWidth -- move our substring into next line obj.x = curX obj.y = curY -- and y-translate group tempGroup.y = tempGroup.y - fontSize/2 end -- reset substring substring = "" -- update currentX curX = curX + obj.contentWidth end -- ========================= -- now lets handle our emoji -- ========================= -- if emoji is variant-indicator if( codepoint == modVariant ) then combinedEmoji = false -- elseif emoji is a male/female modifiers elseif(codepoint == modFemale or codepoint == modMale) then currentEmoji = currentEmoji .. "\_" .. codepoint combinedEmoji = false -- elseif emoji is colored via fitzpatrick scale elseif( codepoint == modLightSkin or codepoint == modMediumLightSkin or codepoint == modMediumSkin or codepoint == modMediumDarkSkin or codepoint == modDarkSkin ) then currentEmoji = currentEmoji .. "\_" .. codepoint combinedEmoji = false -- elseif emoji is a zero-width-joiner elseif(codepoint == modJoin) then -- special case: set combinedEmoji to true so that next emoji can be added -- to create a combined emoji currentEmoji = currentEmoji .. "\_" combinedEmoji = true -- else: emoji is an actual emoji else -- in case previous emoji was not a combined one if(combinedEmoji == false) then -- if there actually is a previous, still unrendered emoji if(currentEmoji ~= "") then -- normal case: We have the emoji in our Sheet if( emojiMap[currentEmoji] ) then emoji = display.newImageRect( tempGroup, sheet, emojiMap[currentEmoji], fontSize, fontSize) emoji.x = curX emoji.y = curY -- fallback: We dont have this emoji, use a standard one of your choice else emoji = display.newImageRect( tempGroup, sheet, emojiMap["128514"], fontSize, fontSize) emoji.x = curX emoji.y = curY end -- set anchorPoint emoji.anchorX = 0 -- update current Width currentWidth = currentWidth + emoji.contentWidth -- check if we passed of max-width (end of line) if(currentWidth \> width) then -- if yes, reset positions for next char / emoji curX = x curY = curY + fontSize -- reset currentWidth currentWidth = emoji.contentWidth -- move our emoji into next line emoji.x = curX emoji.y = curY -- and y-translate group tempGroup.y = tempGroup.y - fontSize/2 end -- update currentX curX = curX + emoji.contentWidth end -- after printing previous emoji, reset currentEmoji currentEmoji = "" end -- with each emoji, reset combinationFlag, -- only gets true once we see a zero-width-joiner combinedEmoji = false currentEmoji = currentEmoji .. codepoint end end end -- after we are done looping through input string, check if there is a remaing substring if(substring ~= "") then -- if yes, print substring local obj = display.newText( tempGroup, substring, curX, curY, font, fontSize ) obj:setFillColor(fillColor) obj.anchorX = 0 -- on iOS, add font-leading to y-center text correct if (system.getInfo( "platform" ) == "ios") then obj.y = obj.y + fontLeading end -- update currentWidth currentWidth = currentWidth + obj.contentWidth -- check if we passed of max-width (end of line) if(currentWidth \> width) then -- move our substring into next line obj.x = x obj.y = obj.y + fontSize -- and y-translate group tempGroup.y = tempGroup.y - fontSize/2 end -- also, check if there is a remaining emoji elseif(currentEmoji ~= "") then -- normal case: We have the emoji in our Sheet if( emojiMap[currentEmoji] ) then emoji = display.newImageRect( tempGroup, sheet, emojiMap[currentEmoji], fontSize, fontSize) emoji.x = curX emoji.y = curY -- fallback: We dont have this emoji, use a standard one of your choice else emoji = display.newImageRect( tempGroup, sheet, emojiMap["128514"], fontSize, fontSize) emoji.x = curX emoji.y = curY end -- set anchorPoint emoji.anchorX = 0 -- update current Width currentWidth = currentWidth + emoji.contentWidth -- check if we passed of max-width (end of line) if(currentWidth \> width) then -- if yes, move our emoji into next line emoji.x = x emoji.y = curY + fontSize -- and y-translate group tempGroup.y = tempGroup.y - fontSize/2 end end end -- fill emoji map -- this needs to be done by user emojiMap["128512"] = 294 emojiMap["128514"] = 42 emojiMap["128515"] = 12 emojiMap["128129"] = 1698 -- return table return emojiModule