Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

Color-Emoji support without native.* really not doable?
Started by CoronaChris Oct 27 2018 08:07 AM

- - - - -
28 replies to this topic

Best Answer CoronaChris , 05 November 2018 - 06:31 AM

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

[TOPIC CONTROLS]
Page 1 of 2 1 2
This topic has been archived. This means that you cannot reply to this topic.
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

CoronaChris

[GLOBAL: userInfoPane.html]
CoronaChris
  • Contributor

  • 162 posts
  • Corona SDK

Dear Corona community,

 

what I'd like to do:

Allow the user to type in not just text, but also colored emojis. Native.newTextBoxes can do that. Then store the input in a mysql database and show it on other devices in a scrollView or container, so basically an absolute requirement in todays social media apps. The strings can be accross multiple lines.

 

what the problems are:

- Display.newText objects dont support colored emojis (only black and white).

- webviews reside on top of other stuff, so I cannot use them in a scrollview for example

- same with native.newtextboxes, they also sit on top of other stuff

- Splitting strings at some sort of markers within and inject images in between doesnt really work either because I cannot extract the .x - position of those markers

 

so my question is:

Is there any way to support colored emojis in Corona within a user-input string? Like, ANY way? I'd even consider hiring someone to write a plugin if there is anyone out there who says, yeah, I can do that :)

 

/edit: This is how it looks like when you have a native.newTextField on top and copy its input into a display.newText() below:

 

 

EmoijFail.jpg

 

 

Here is zipped project with a very simple demonstration: Attached File  emoji.zip   52.34KB   46 downloads

 

 

Would appreciate any ideas or hints.

 

Thanks,

 

Chris



[TOPIC: post.html]
#2

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,606 posts
  • Corona SDK

No & Yes

No, not through Corona's built-in display.* features directly.

Yes, write your own layer over display.newText() (or follow my suggestion below)

I'm not saying it will be easy, but you can do it.

You'll need emoji art to support this.

Suggestion
- Your best bet is to download a BitMapped font library you can use and understand. 

- Generate a set of fonts to use with it.

- Modify the module/library to support emojis.

- Supply a standard set of emojis.

 

 

Note: I believe, you'll you'll need to support unicode.  This could be a bit of work.

 

 

PS - Thanks for the downloadable example.  You did however forget to include the font in you example, and I don't see how this demonstrates the issue.



[TOPIC: post.html]
#3

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,606 posts
  • Corona SDK

Question, how do you expect the text to be provided? 

 

i.e. Is this entirely internal with an arbitrary encoding for the emojis or are you trying to make something that can take text input from the users's device that is then displayed as a text object with the same emoji's used on their device?

 

I mean do you expect this to work:

 

1. User types in some text and emojis into native.newTextField().

 

2. You extract the 'string' from the text field.

 

3. You display it using Corona's display.new*() features.  



[TOPIC: post.html]
#4

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,606 posts
  • Corona SDK

Download this to see an hacky solution and tool I made to demonstrate the singular fact that 'it can (almost) always be done with Corona':
 
https://github.com/roaminggamer/RG_FreeStuff/raw/master/AskEd/2018/10/emojis.zip
 
(emoji_mod subfolder)
 
Run using a simulated android device for mouse hover to work.
 
 
>>> If you like my effort click here:  Thanks RoamingGamer!
 
Image below shows demo running.
 
Image description top-to-bottom:

  • line 1 - What you get when printing strings containing emoji using display.newText()
  • line 2 - The output of my hacky extension display.newEmojiText()
  • remainder - A tool for getting sheet index for emojis from a sheet of emojis I found online.


emojis.png
 
 
 
PS - I know  those are probably NOT the right emojis.  I randomly selected two for the mapping.


Edited by roaminggamer, 27 October 2018 - 12:43 PM.


[TOPIC: post.html]
#5

CoronaChris

[GLOBAL: userInfoPane.html]
CoronaChris
  • Contributor

  • 162 posts
  • Corona SDK

Ed, it's just always a pleasure reading your replies to either my or someone else's questions. It's not that often that people put that much effort into a reply and even upload a demo of a - hacky or not - solution. I've said it many times: Thank you :)

 

I've downloaded your mod and I'm going to try to understand every single line and function tomorrow - just too tired right now, too many hours of coding :)

 

After having a quick look it seems that your newEmojiText function sort of splits an input text into multiple parts: "Normal" characters are treated with display.newText - objects, emojis instead are added via newImageRect right next to the last string by setting curX + tmp.contentWidth.

 

I had something similar in mind, but stumbled when the userinput is longer (wider) than display.contentWidth (or any width value specified) since then tmp.contentWidth needs to be fixed in advance. So, I guess the demo project is a very good start, the next challenge though would be to modify it for multiline input. Hope it gets clear what I am trying to explain.

 

And, to answer your questions:

 

How does my demo project demonstrate the issue?

When you run it on a device and type in an emoji via the normal keyboard, you get the result my screenshot shows. The missing font is the result of my copy and paste process to make it a mini-version, but that shouldnt be a problem since devices just use a standard font instead.

 

how do you expect the text to be provided?

- User types in some text and emojis into native.newTextBox().

- This input is stored in a module and is retreived in another scene where it is - or should be - put into a scrollview

- If user is happy with his input, the input is send to my Google App Engine where it's stored in a mysql database (utf8)

- other devices can request this input and show it again in a scrollview, so yes, display it using Corona's display.new*() features

- The input can be multiline

- it would be perfect if the user could use all the emojis his keyboard offers

 

 

I'll have a closer look tomorrow, but if this sounds like a 'hire a hitman' project for you and you feel like you could solve this even for newTextBox - inputs, please let me know :)



[TOPIC: post.html]
#6

CoronaChris

[GLOBAL: userInfoPane.html]
CoronaChris
  • Contributor

  • 162 posts
  • Corona SDK

Argh, this whole thing wont let me sleep. Just made your hacky solution even dirtier :)

 

I modified your newEmojiText a little bit and

 

- added currentWidth = 0

- added currentY = 0

 

Now, with every Character or emoji, I add their contentWidth to currentWidth. As soon as currentWidth is equal or more than my width-limit, I set back currentWidth to 0 and currentY to group.contentHeight. This way, the next character is added into the next line.

 

Guess you're right, this whole thing might be doable with pure Corona, even though there is - of course - a lot of improvements and work to do.

 

For the moment I am happy though that for a longer uniString in your examle I did get a multiline result with smiling emojis. Good night :)



[TOPIC: post.html]
#7

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,606 posts
  • Corona SDK

After the alignment issues, the sheer number of emojis to map is daunting.  I do suspect however that someone out there must have made a collection of emojis or a sheet that is ordered to match the standard emojis list (assuming there is such a thing.)

 

Whatever the case, hacky solution is far from perfect, but I do think it will make a first version that can be refined over and over to suit your needs.

 

Good luck with this.



[TOPIC: post.html]
#8

CoronaChris

[GLOBAL: userInfoPane.html]
CoronaChris
  • Contributor

  • 162 posts
  • Corona SDK

Played around with it this morning and I am facing tthree main issues here:

 

1) A user types in an emoji that his keyboard offers. Later, this emoji is replaced by the corresponding emoji from an image sheet. They do look similar, but I do see young people claiming: "Wait a minute, that's not exactly what I typed"

 

2) y-Alignment within one line: display.newText(...) and display.newImageRect are perfect on the same y-level in simulator, but accross testdevices they're not. I know this has something to do with rendering of the newText, and if I remember correctly you can never tell the exact y-position of a text-element.

 

3) Each input string right now is split into a display-object for each and every character (char or emoji). So, for an input string like "Hello :) how are we doing today ;)" we get like 30 display-objects. Now, I have a scrollview that shows many of those input strings, so I'd get 300 objects (lets keep it simple) for ten strings. At least to me this seems like this is not good at all memory-wise.

 

Any ideas how this could be approached?



[TOPIC: post.html]
#9

Arteficio

[GLOBAL: userInfoPane.html]
Arteficio
  • Enthusiast

  • 83 posts
  • Corona SDK

3) once you have built your row or the whole paragraph just take a snapshot of it and use the snapshot in the scrollview

[TOPIC: post.html]
#10

CoronaChris

[GLOBAL: userInfoPane.html]
CoronaChris
  • Contributor

  • 162 posts
  • Corona SDK

...and then remove all the other objects that have been created before I guess!

 

Good point Arteficio, that would indeed be an option to solve at least problem nr. 3 :)

Thank you for your input :)



[TOPIC: post.html]
#11

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,606 posts
  • Corona SDK

Hi again.  That was about all I had to offer.  

 

re: #2 - Yes.  y-alignment will be an issue. This is why I suggest using and then modifying an existing Bitmapped text library (PonyFont or other) instead of plain text.  Bitmapped fonts should all be pre-aligned.

 

#3 I did letter by letter, but you can actually accumulate all letters and spaces into one text object till you hit an emoji.

 

My plain text hack is just a starting-point/concept for a real solution.



[TOPIC: post.html]
#12

CoronaChris

[GLOBAL: userInfoPane.html]
CoronaChris
  • Contributor

  • 162 posts
  • Corona SDK

I skyped with Hassaan today and we discussed the whole thing in detail. He agress that your suggestion of using a bitmap font might be my best option here. I already downloaded PonyFont, found a .ttf file with all emojis in there (EmojiOne 4.0) and am currently trying to make bitmap font out of their ttf using Glyph Designer. Right now I've now idea how to get this work, but I'll get there :)



[TOPIC: post.html]
#13

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,606 posts
  • Corona SDK

I am not suggesting you use PonyFont unmodified.

 

I do not think you will be able to get colored Emojis and bitmapped fonts.

 

You will need to modify PonyFont to identify emojies and instead of using a letter texture, use a emoji image from the sheet like I did.

 

i.e. You probably cannot have a single sheet of letters and emojis.  No tool for creating Font Sheets that I know of works that way.



[TOPIC: post.html]
#14

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,606 posts
  • Corona SDK

Final note regarding y-align.

 

Did you try to use baselineOffset?

 

https://docs.coronalabs.com/api/type/TextObject/baselineOffset.html

 

With that info you may be able to get by with my hacky solution and the addition of taking a snapshot to reduce the number of total objects.



[TOPIC: post.html]
#15

CoronaChris

[GLOBAL: userInfoPane.html]
CoronaChris
  • Contributor

  • 162 posts
  • Corona SDK

Ok then, seems like I misunderstood something. Wow, this whole thing is like the biggest challenge for me so far in Corona. Guess I'll take a deep look into PonyFont and try to modify it the way you suggested.

 

And - concerning your second question: Yes, I came accross baselineOffset today, but then switched to the bitmap font solution. I now have those two options, we'll see if I can make one work :)



[TOPIC: post.html]
#16

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,606 posts
  • Corona SDK

I'd experiment with baselineOffset first.
 
That is a few minutes of work.
 
Modifying PonyFont will be much more time.
 
 
Adding baselineOffset should literally be changing this:
 

tmp = display.newText( text, string.char(codepoint), curX, 0, font, fontSize )
tmp:translate( 0, tmp.baselineOffset )

to this:
 

tmp = display.newText( text, string.char(codepoint), curX, 0, font, fontSize )
tmp:translate( 0, tmp.baselineOffset )

PASTED WRONG CODE ORIGINALLY


Edited by roaminggamer, 28 October 2018 - 07:52 AM.


[TOPIC: post.html]
#17

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,606 posts
  • Corona SDK

Ugh, now I am curious and have to find out.... I'll check back in a bit.



[TOPIC: post.html]
#18

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,606 posts
  • Corona SDK

OK, I'm not 100% sure, but this might be it:

tmp = display.newText( text, string.char(codepoint), curX, 0, font, fontSize )			
tmp:translate( 0, tmp.baselineOffset * tmp.anchorY )

I have not tested this on device.

 

Here is an update to my demo (I used a TTF to test):

https://github.com/roaminggamer/RG_FreeStuff/raw/master/AskEd/2018/10/emojis.zip (same link; download again)

 

I also tweaked the function to allow you to provide an 'alternate' emoji' size to make the fit better.



[TOPIC: post.html]
#19

CoronaChris

[GLOBAL: userInfoPane.html]
CoronaChris
  • Contributor

  • 162 posts
  • Corona SDK

I tried it with baselineOffset * 0.5 this morning, not really understanding why the *0.5 needed to be included, but your  * tmp.anchorY now makes sense. Yes, it does work on my Huawei P20. The emojis are a little bit bigger than a single char, but their y-alignment looks good and I guess the size for emojis should be easily customizable (fontSize * 0.9 or something).

 

One step closer :)

 

So, right now I am thinking of providing two imageSheets: One with iOS emojis, one for Android (ignoring copyright for the moment, I'll check that out later). Same mapping table though. This way, iPhone user would see their apple emojis, android users would receive the same emoji in their familiar Android way.

 

So, step by step :

 

1) User types in using the standard iOS or Android keyboard into a native.newTextBox()

2) User sees his "normal" emojis since its all native. String is stored in a module

3) In other scenes I fetch the string and shoot it into your newEmojiText function.

4) Within this function, the string is split into single elements (chars and emojis)

5) for chars, use display.newText, for emojis use a spriteSheet (iOS or Android) and newImageRects.

6) add those chars and symbols next to each other, or if end of line (specified limit) is reached, change curY and reset curX to 0.

7) This way, all emojis are presented the way the user is familiar with and all should look fine.

 

Sounds not too bad to me. I'll send another beer via paypal if that works out  :)



[TOPIC: post.html]
#20

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,606 posts
  • Corona SDK

Good idea on the per-platform emoji sheets.  Remember to make one of them the default in case of a failure to identify the OS.

 

I keep thinking there should be some clever way to get the emojis by downloading them or something, but making a sheet or finding one is probably the safest bet.

 

Of course, the remapping is going to be a hassle if nobody has a ordered sheet already.

 

Tip: Don't forget to provide a nice way for handling 'unknown'/'unmapped' emojis IDs.  Right now I just make a unfilled white rectangle, but you might want to fill with a transparent texture or default texture instead.



[TOPIC: post.html]
#21

CoronaChris

[GLOBAL: userInfoPane.html]
CoronaChris
  • Contributor

  • 162 posts
  • Corona SDK

Excellent input, I'll consider your tips in my final solution (which will be posted here once it's done)...



[TOPIC: post.html]
#22

ojnab

[GLOBAL: userInfoPane.html]
ojnab
  • Contributor

  • 588 posts
  • Corona SDK

Great thread.  Please share if you come up with a solution. Thanks  :)



[TOPIC: post.html]
#23

CoronaChris

[GLOBAL: userInfoPane.html]
CoronaChris
  • Contributor

  • 162 posts
  • Corona SDK

Will do, promised. My graphic designer is currently trying to understand potential copyright issues with iOS / Android emojis and where to get them and I just discovered that our German öäü Umlaute dont work anymore, but we'll get there :)

 

Busy week with many clients to take care of, but a working solution will be posted here!

 

 

/edit: Umlaut-problem fixed by changing

-- if next object is a char
if( codepoint <= 255 ) then	

        -- create a newText object with the single char
	obj = display.newText( tempGroup, string.char(codepoint), curX, curY, font, fontSize )
			

to

-- if next object is a char
if( codepoint <= 255 ) then	

	-- create a newText object with the single char
	obj = display.newText( tempGroup, utf8.char(codepoint), curX, curY, font, fontSize )

So, it has to be utft8.char(codepoint), not string.char(codepoint).

 

Funny thing is: ÄäÖÜ did work, but öü didnt on my testdevice. Now they all do :)



[TOPIC: post.html]
#24

CoronaChris

[GLOBAL: userInfoPane.html]
CoronaChris
  • Contributor

  • 162 posts
  • Corona SDK

New issue observed: As long as each character is printed separately via display.newText(singleCharacter, ...), the distance between those characters isn't correct any more. They all have - of course - the same distance between them, which looks weird when there is "T" involved for example. In the word "Test", one would expect the "e" to move closer to the stem of the "T", which wouldn't happen in the current version, it'd rather look like "T e".

 

Solved it by building up a substring and only print it via display.newText(substring, ...) as soon as we see a space, an emoji or we reach end of specified with. This way, it should be even faster and less memory consuming while the whole transformation is in progress since we build way less newText-objects (even if we delete them after taking a snapshot).

 

Works like a charme, now distance between chars isnt an issue any more :)



[TOPIC: post.html]
#25

CoronaChris

[GLOBAL: userInfoPane.html]
CoronaChris
  • Contributor

  • 162 posts
  • Corona SDK

  Best Answer

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




[topic_controls]
Page 1 of 2 1 2
 
[/topic_controls]