diff options
| author | Callum Linden <callum@lindenlab.com> | 2023-01-20 16:23:41 -0800 | 
|---|---|---|
| committer | Callum Linden <callum@lindenlab.com> | 2023-01-20 16:23:41 -0800 | 
| commit | cbb7480ca76cbe11c40f05a269583201dd044796 (patch) | |
| tree | 8ce39d5a975d66a5c6a38e47460c2330f15a087e /indra | |
| parent | 80bc32c3efe77b49adcd4cc00320ce5523f90fb9 (diff) | |
| parent | 12204b210e5279b0bfba5601d34195739cab6ab8 (diff) | |
Fix merge conflicts after auto merge
Diffstat (limited to 'indra')
61 files changed, 13080 insertions, 423 deletions
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 4d70089737..31cd5d770d 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -27,6 +27,7 @@ set(cmake_SOURCE_FILES      FindAutobuild.cmake      FindGLH.cmake      FindHUNSPELL.cmake +    FindICU4C.cmake      FindJsonCpp.cmake      FindNDOF.cmake      FindOpenJPEG.cmake @@ -42,6 +43,7 @@ set(cmake_SOURCE_FILES      GoogleMock.cmake      Havok.cmake      Hunspell.cmake +    ICU4C.cmake      JPEG.cmake      JsonCpp.cmake      LLAddBuildTest.cmake diff --git a/indra/cmake/FindICU4C.cmake b/indra/cmake/FindICU4C.cmake new file mode 100644 index 0000000000..327d761a88 --- /dev/null +++ b/indra/cmake/FindICU4C.cmake @@ -0,0 +1,33 @@ +# -*- cmake -*- + +# - Find ICU4C +# This module defines +#  ICU4C_INCLUDE_DIR, where to find headers +#  ICU4C_LIBRARY, the library needed to use ICU4C. +#  ICU4C_FOUND, If false, do not try to use ICU4C. + +find_path(ICU4C_INCLUDE_DIR uchar.h +  PATH_SUFFIXES unicode +  ) + +set(ICU4C_NAMES ${ICU4C_NAMES} icuuc) +find_library(ICU4C_LIBRARY +  NAMES ${ICU4C_NAMES} +  ) + +if (ICU4C_LIBRARY AND ICU4C_INCLUDE_DIR) +  set(ICU4C_FOUND "YES") +else (ICU4C_LIBRARY AND ICU4C_INCLUDE_DIR) +  set(ICU4C_FOUND "NO") +endif (ICU4C_LIBRARY AND ICU4C_INCLUDE_DIR) + +if (ICU4C_FOUND) +    message(STATUS "Found ICU4C: Library in '${ICU4C_LIBRARY}' and header in '${ICU4C_INCLUDE_DIR}' ") +else (ICU4C_FOUND) +    message(FATAL_ERROR " * * *\nCould not find ICU4C library! * * *") +endif (ICU4C_FOUND) + +mark_as_advanced( +  ICU4C_LIBRARY +  ICU4C_INCLUDE_DIR +  ) diff --git a/indra/cmake/ICU4C.cmake b/indra/cmake/ICU4C.cmake new file mode 100644 index 0000000000..007a9b6937 --- /dev/null +++ b/indra/cmake/ICU4C.cmake @@ -0,0 +1,22 @@ +# -*- cmake -*- +include(Prebuilt) + +set(ICU4C_FIND_QUIETLY ON) +set(ICU4C_FIND_REQUIRED ON) + +if (USESYSTEMLIBS) +  include(FindICU4C) +else (USESYSTEMLIBS) +  use_prebuilt_binary(icu4c) +  if (WINDOWS) +    set(ICU4C_LIBRARY icuuc) +  #elseif(DARWIN) +  #  set(ICU4C_LIBRARY ...) +  #elseif(LINUX) +  #  set(ICU4C_LIBRARY ...) +  else() +    message(FATAL_ERROR "Invalid platform") +  endif() +  set(ICU4C_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/unicode) +  use_prebuilt_binary(dictionaries) +endif (USESYSTEMLIBS) diff --git a/indra/cmake/ViewerMiscLibs.cmake b/indra/cmake/ViewerMiscLibs.cmake index fc5bdedb5a..9b365c159a 100644 --- a/indra/cmake/ViewerMiscLibs.cmake +++ b/indra/cmake/ViewerMiscLibs.cmake @@ -10,3 +10,5 @@ if (NOT USESYSTEMLIBS)    use_prebuilt_binary(slvoice)  endif(NOT USESYSTEMLIBS) +use_prebuilt_binary(nanosvg) +use_prebuilt_binary(viewer-fonts) diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 108149b5f7..3371306f11 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -3,6 +3,7 @@  project(llcommon)  include(00-Common) +include(ICU4C)  include(LLCommon)  include(bugsplat)  include(Linking) @@ -304,6 +305,7 @@ target_link_libraries(      ${APRUTIL_LIBRARIES}      ${APR_LIBRARIES}      ${EXPAT_LIBRARIES} +    ${ICU4C_LIBRARY}      ${JSONCPP_LIBRARIES}      ${ZLIBNG_LIBRARIES}      ${WINDOWS_LIBRARIES} diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index 7f501f2e77..5da3609b43 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -30,6 +30,7 @@  #include "llerror.h"  #include "llfasttimer.h"  #include "llsd.h" +#include <unicode/uchar.h>  #include <vector>  #if LL_WINDOWS @@ -833,6 +834,31 @@ std::string LLStringOps::sDayFormat;  std::string LLStringOps::sAM;  std::string LLStringOps::sPM; +// static +bool LLStringOps::isEmoji(llwchar wch) +{ +	switch (ublock_getCode(wch)) +	{ +		case UBLOCK_MISCELLANEOUS_SYMBOLS: +		case UBLOCK_DINGBATS: +		case UBLOCK_MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS: +		case UBLOCK_EMOTICONS: +		case UBLOCK_TRANSPORT_AND_MAP_SYMBOLS: +#if U_ICU_VERSION_MAJOR_NUM > 56 +		// Boost uses ICU so we can't update it independently +		case UBLOCK_SUPPLEMENTAL_SYMBOLS_AND_PICTOGRAPHS: +#endif // U_ICU_VERSION_MAJOR_NUM > 56 +			return true; +		default: +#if U_ICU_VERSION_MAJOR_NUM > 56 +			return false; +#else +			// See https://en.wikipedia.org/wiki/Supplemental_Symbols_and_Pictographs +			return wch >= 0x1F900 && wch <= 0x1F9FF; +#endif // U_ICU_VERSION_MAJOR_NUM > 56 +	} +} +  S32	LLStringOps::collate(const llwchar* a, const llwchar* b)  {  diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index d94f549480..093d7cb9ab 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -189,6 +189,8 @@ public:  	static bool isAlnum(char a) { return isalnum((unsigned char)a) != 0; }  	static bool isAlnum(llwchar a) { return iswalnum(a) != 0; } +	static bool isEmoji(llwchar wch); +  	static S32	collate(const char* a, const char* b) { return strcoll(a, b); }  	static S32	collate(const llwchar* a, const llwchar* b); diff --git a/indra/llrender/CMakeLists.txt b/indra/llrender/CMakeLists.txt index baab09a104..a82ab2e523 100644 --- a/indra/llrender/CMakeLists.txt +++ b/indra/llrender/CMakeLists.txt @@ -33,6 +33,7 @@ set(llrender_SOURCE_FILES      llcubemap.cpp      llfontbitmapcache.cpp      llfontfreetype.cpp +    llfontfreetypesvg.cpp      llfontgl.cpp      llfontregistry.cpp      llgl.cpp @@ -60,6 +61,7 @@ set(llrender_HEADER_FILES      llcubemap.h      llfontgl.h      llfontfreetype.h +    llfontfreetypesvg.h      llfontbitmapcache.h      llfontregistry.h      llgl.h diff --git a/indra/llrender/llfontbitmapcache.cpp b/indra/llrender/llfontbitmapcache.cpp index c71e24c83a..8809f9f475 100644 --- a/indra/llrender/llfontbitmapcache.cpp +++ b/indra/llrender/llfontbitmapcache.cpp @@ -30,14 +30,7 @@  #include "llfontbitmapcache.h"  LLFontBitmapCache::LLFontBitmapCache() -:	mNumComponents(0), -	mBitmapWidth(0), -	mBitmapHeight(0), -	mBitmapNum(-1), -	mMaxCharWidth(0), -	mMaxCharHeight(0), -	mCurrentOffsetX(1), -	mCurrentOffsetY(1) +  {  } @@ -45,121 +38,135 @@ LLFontBitmapCache::~LLFontBitmapCache()  {  } -void LLFontBitmapCache::init(S32 num_components, -							 S32 max_char_width, +void LLFontBitmapCache::init(S32 max_char_width,  							 S32 max_char_height)  {  	reset(); -	mNumComponents = num_components;  	mMaxCharWidth = max_char_width;  	mMaxCharHeight = max_char_height; + +	S32 image_width = mMaxCharWidth * 20; +	S32 pow_iw = 2; +	while (pow_iw < image_width) +	{ +		pow_iw <<= 1; +	} +	image_width = pow_iw; +	image_width = llmin(512, image_width); // Don't make bigger than 512x512, ever. + +	mBitmapWidth = image_width; +	mBitmapHeight = image_width;  } -LLImageRaw *LLFontBitmapCache::getImageRaw(U32 bitmap_num) const +LLImageRaw *LLFontBitmapCache::getImageRaw(EFontGlyphType bitmap_type, U32 bitmap_num) const  { -	if (bitmap_num >= mImageRawVec.size()) -		return NULL; +	const U32 bitmap_idx = static_cast<U32>(bitmap_type); +	if (bitmap_type >= EFontGlyphType::Count || bitmap_num >= mImageRawVec[bitmap_idx].size()) +		return nullptr; -	return mImageRawVec[bitmap_num]; +	return mImageRawVec[bitmap_idx][bitmap_num];  } -LLImageGL *LLFontBitmapCache::getImageGL(U32 bitmap_num) const +LLImageGL *LLFontBitmapCache::getImageGL(EFontGlyphType bitmap_type, U32 bitmap_num) const  { -	if (bitmap_num >= mImageGLVec.size()) -		return NULL; +	const U32 bitmap_idx = static_cast<U32>(bitmap_type); +	if (bitmap_type >= EFontGlyphType::Count || bitmap_num >= mImageGLVec[bitmap_idx].size()) +		return nullptr; -	return mImageGLVec[bitmap_num]; +	return mImageGLVec[bitmap_idx][bitmap_num];  } -BOOL LLFontBitmapCache::nextOpenPos(S32 width, S32 &pos_x, S32 &pos_y, S32& bitmap_num) +BOOL LLFontBitmapCache::nextOpenPos(S32 width, S32& pos_x, S32& pos_y, EFontGlyphType bitmap_type, U32& bitmap_num)  { -	if ((mBitmapNum<0) || (mCurrentOffsetX + width + 1) > mBitmapWidth) +	if (bitmap_type >= EFontGlyphType::Count) +	{ +		return FALSE; +	} + +	const U32 bitmap_idx = static_cast<U32>(bitmap_type); +	if (mImageRawVec[bitmap_idx].empty() || (mCurrentOffsetX[bitmap_idx] + width + 1) > mBitmapWidth)  	{ -		if ((mBitmapNum<0) || (mCurrentOffsetY + 2*mMaxCharHeight + 2) > mBitmapHeight) +		if ((mImageRawVec[bitmap_idx].empty()) || (mCurrentOffsetY[bitmap_idx] + 2*mMaxCharHeight + 2) > mBitmapHeight)  		{  			// We're out of space in the current image, or no image  			// has been allocated yet.  Make a new one. -			 -			mImageRawVec.push_back(new LLImageRaw); -			mBitmapNum = mImageRawVec.size()-1; -			LLImageRaw *image_raw = getImageRaw(mBitmapNum); +			S32 num_components = getNumComponents(bitmap_type); +			mImageRawVec[bitmap_idx].push_back(new LLImageRaw(mBitmapWidth, mBitmapHeight, num_components)); +			bitmap_num = mImageRawVec[bitmap_idx].size() - 1; -			// Make corresponding GL image. -			mImageGLVec.push_back(new LLImageGL(FALSE)); -			LLImageGL *image_gl = getImageGL(mBitmapNum); -			 -			S32 image_width = mMaxCharWidth * 20; -			S32 pow_iw = 2; -			while (pow_iw < image_width) +			LLImageRaw* image_raw = getImageRaw(bitmap_type, bitmap_num); +			if (EFontGlyphType::Grayscale == bitmap_type)  			{ -				pow_iw *= 2; +				image_raw->clear(255, 0);  			} -			image_width = pow_iw; -			image_width = llmin(512, image_width); // Don't make bigger than 512x512, ever. -			S32 image_height = image_width; -			image_raw->resize(image_width, image_height, mNumComponents); - -			mBitmapWidth = image_width; -			mBitmapHeight = image_height; - -			switch (mNumComponents) -			{ -				case 1: -					image_raw->clear(); -				break; -				case 2: -					image_raw->clear(255, 0); -				break; -			} +			// Make corresponding GL image. +			mImageGLVec[bitmap_idx].push_back(new LLImageGL(image_raw, false)); +			LLImageGL* image_gl = getImageGL(bitmap_type, bitmap_num);  			// Start at beginning of the new image. -			mCurrentOffsetX = 1; -			mCurrentOffsetY = 1; +			mCurrentOffsetX[bitmap_idx] = 1; +			mCurrentOffsetY[bitmap_idx] = 1; -			// Attach corresponding GL texture. -			image_gl->createGLTexture(0, image_raw); +			// Attach corresponding GL texture. (*TODO: is this needed?)  			gGL.getTexUnit(0)->bind(image_gl);  			image_gl->setFilteringOption(LLTexUnit::TFO_POINT); // was setMipFilterNearest(TRUE, TRUE);  		}  		else  		{  			// Move to next row in current image. -			mCurrentOffsetX = 1; -			mCurrentOffsetY += mMaxCharHeight + 1; +			mCurrentOffsetX[bitmap_idx] = 1; +			mCurrentOffsetY[bitmap_idx] += mMaxCharHeight + 1;  		}  	} -	pos_x = mCurrentOffsetX; -	pos_y = mCurrentOffsetY; -	bitmap_num = mBitmapNum; +	pos_x = mCurrentOffsetX[bitmap_idx]; +	pos_y = mCurrentOffsetY[bitmap_idx]; +	bitmap_num = getNumBitmaps(bitmap_type) - 1; -	mCurrentOffsetX += width + 1; +	mCurrentOffsetX[bitmap_idx] += width + 1;  	return TRUE;  }  void LLFontBitmapCache::destroyGL()  { -	for (std::vector<LLPointer<LLImageGL> >::iterator it = mImageGLVec.begin(); -		 it != mImageGLVec.end(); ++it) +	for (U32 idx = 0, cnt = static_cast<U32>(EFontGlyphType::Count); idx < cnt; idx++)  	{ -		(*it)->destroyGLTexture(); +		for (LLImageGL* image_gl : mImageGLVec[idx]) +		{ +			image_gl->destroyGLTexture(); +		}  	}  }  void LLFontBitmapCache::reset()  { -	mImageRawVec.clear(); - -	mImageGLVec.clear(); +	for (U32 idx = 0, cnt = static_cast<U32>(EFontGlyphType::Count); idx < cnt; idx++) +	{ +		mImageRawVec[idx].clear(); +		mImageGLVec[idx].clear(); +		mCurrentOffsetX[idx] = 1; +		mCurrentOffsetY[idx] = 1; +	}  	mBitmapWidth = 0;  	mBitmapHeight = 0; -	mBitmapNum = -1; -	mCurrentOffsetX = 1; -	mCurrentOffsetY = 1;  } +//static +U32 LLFontBitmapCache::getNumComponents(EFontGlyphType bitmap_type) +{ +	switch (bitmap_type) +	{ +		case EFontGlyphType::Grayscale: +			return 2; +		case EFontGlyphType::Color: +			return 4; +		default: +			llassert(false); +			return 2; +	} +} diff --git a/indra/llrender/llfontbitmapcache.h b/indra/llrender/llfontbitmapcache.h index 7de3a6b56f..c63281ab70 100644 --- a/indra/llrender/llfontbitmapcache.h +++ b/indra/llrender/llfontbitmapcache.h @@ -30,6 +30,14 @@  #include <vector>  #include "lltrace.h" +enum class EFontGlyphType : U32 +{ +	Grayscale = 0, +	Color, +	Count, +	Unspecified, +}; +  // Maintain a collection of bitmaps containing rendered glyphs.  // Generalizes the single-bitmap logic from LLFontFreetype and LLFontGL.  class LLFontBitmapCache @@ -39,35 +47,35 @@ public:  	~LLFontBitmapCache();  	// Need to call this once, before caching any glyphs. - 	void init(S32 num_components, -			  S32 max_char_width, + 	void init(S32 max_char_width,  			  S32 max_char_height);  	void reset(); -	BOOL nextOpenPos(S32 width, S32 &posX, S32 &posY, S32 &bitmapNum); +	BOOL nextOpenPos(S32 width, S32& posX, S32& posY, EFontGlyphType bitmapType, U32& bitmapNum);  	void destroyGL(); - 	LLImageRaw *getImageRaw(U32 bitmapNum = 0) const; - 	LLImageGL *getImageGL(U32 bitmapNum = 0) const; -	 + 	LLImageRaw* getImageRaw(EFontGlyphType bitmapType, U32 bitmapNum) const; + 	LLImageGL* getImageGL(EFontGlyphType bitmapType, U32 bitmapNum) const; +  	S32 getMaxCharWidth() const { return mMaxCharWidth; } -	S32 getNumComponents() const { return mNumComponents; } +	U32 getNumBitmaps(EFontGlyphType bitmapType) const { return (bitmapType < EFontGlyphType::Count) ? mImageRawVec[static_cast<U32>(bitmapType)].size() : 0; }  	S32 getBitmapWidth() const { return mBitmapWidth; }  	S32 getBitmapHeight() const { return mBitmapHeight; } +protected: +	static U32 getNumComponents(EFontGlyphType bitmap_type); +  private: -	S32 mNumComponents; -	S32 mBitmapWidth; -	S32 mBitmapHeight; -	S32 mBitmapNum; -	S32 mMaxCharWidth; -	S32 mMaxCharHeight; -	S32 mCurrentOffsetX; -	S32 mCurrentOffsetY; -	std::vector<LLPointer<LLImageRaw> >	mImageRawVec; -	std::vector<LLPointer<LLImageGL> > mImageGLVec; +	S32 mBitmapWidth = 0; +	S32 mBitmapHeight = 0; +	S32 mCurrentOffsetX[static_cast<U32>(EFontGlyphType::Count)] = { 1 }; +	S32 mCurrentOffsetY[static_cast<U32>(EFontGlyphType::Count)] = { 1 }; +	S32 mMaxCharWidth = 0; +	S32 mMaxCharHeight = 0; +	std::vector<LLPointer<LLImageRaw>> mImageRawVec[static_cast<U32>(EFontGlyphType::Count)]; +	std::vector<LLPointer<LLImageGL>> mImageGLVec[static_cast<U32>(EFontGlyphType::Count)];  };  #endif //LL_LLFONTBITMAPCACHE_H diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp index e964d1586f..5535c07615 100644 --- a/indra/llrender/llfontfreetype.cpp +++ b/indra/llrender/llfontfreetype.cpp @@ -34,14 +34,17 @@  #ifdef LL_WINDOWS  #include <freetype2\freetype\ftsystem.h>  #endif +#include "llfontfreetypesvg.h"  // For some reason, this won't work if it's not wrapped in the ifdef  #ifdef FT_FREETYPE_H  #include FT_FREETYPE_H  #endif +#include "lldir.h"  #include "llerror.h"  #include "llimage.h" +#include "llimagepng.h"  //#include "llimagej2c.h"  #include "llmath.h"	// Linden math  #include "llstring.h" @@ -49,6 +52,8 @@  #include "llfontbitmapcache.h"  #include "llgl.h" +#define ENABLE_OT_SVG_SUPPORT +  FT_Render_Mode gFontRenderMode = FT_RENDER_MODE_NORMAL;  LLFontManager *gFontManagerp = NULL; @@ -81,6 +86,16 @@ LLFontManager::LLFontManager()  		LL_ERRS() << "Freetype initialization failure!" << LL_ENDL;  		FT_Done_FreeType(gFTLibrary);  	} + +#ifdef ENABLE_OT_SVG_SUPPORT +	SVG_RendererHooks hooks = { +		LLFontFreeTypeSvgRenderer::OnInit, +		LLFontFreeTypeSvgRenderer::OnFree, +		LLFontFreeTypeSvgRenderer::OnRender, +		LLFontFreeTypeSvgRenderer::OnPresetGlypthSlot, +	}; +	FT_Property_Set(gFTLibrary, "ot-svg", "svg-hooks", &hooks); +#endif  }  LLFontManager::~LLFontManager() @@ -89,8 +104,9 @@ LLFontManager::~LLFontManager()  } -LLFontGlyphInfo::LLFontGlyphInfo(U32 index) +LLFontGlyphInfo::LLFontGlyphInfo(U32 index, EFontGlyphType glyph_type)  :	mGlyphIndex(index), +	mGlyphType(glyph_type),  	mWidth(0),			// In pixels  	mHeight(0),			// In pixels  	mXAdvance(0.f),		// In pixels @@ -99,10 +115,25 @@ LLFontGlyphInfo::LLFontGlyphInfo(U32 index)  	mYBitmapOffset(0), 	// Offset to the origin in the bitmap  	mXBearing(0),		// Distance from baseline to left in pixels  	mYBearing(0),		// Distance from baseline to top in pixels -	mBitmapNum(0) // Which bitmap in the bitmap cache contains this glyph +	mBitmapEntry(std::make_pair(EFontGlyphType::Unspecified, -1)) // Which bitmap in the bitmap cache contains this glyph  {  } +LLFontGlyphInfo::LLFontGlyphInfo(const LLFontGlyphInfo& fgi) +	: mGlyphIndex(fgi.mGlyphIndex) +	, mGlyphType(fgi.mGlyphType) +	, mWidth(fgi.mWidth) +	, mHeight(fgi.mHeight) +	, mXAdvance(fgi.mXAdvance) +	, mYAdvance(fgi.mYAdvance) +	, mXBitmapOffset(fgi.mXBitmapOffset) +	, mYBitmapOffset(fgi.mYBitmapOffset) +	, mXBearing(fgi.mXBearing) +	, mYBearing(fgi.mYBearing) +{ +	mBitmapEntry = fgi.mBitmapEntry; +} +  LLFontFreetype::LLFontFreetype()  :	mFontBitmapCachep(new LLFontBitmapCache),  	mAscender(0.f), @@ -156,7 +187,7 @@ void ft_close_cb(FT_Stream stream) {  }  #endif -BOOL LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, BOOL is_fallback, S32 face_n) +BOOL LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, bool is_fallback, S32 face_n)  {  	// Don't leak face objects.  This is also needed to deal with  	// changed font file names. @@ -220,7 +251,7 @@ BOOL LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 v  	S32 max_char_width = ll_round(0.5f + (x_max - x_min));  	S32 max_char_height = ll_round(0.5f + (y_max - y_min)); -	mFontBitmapCachep->init(components, max_char_width, max_char_height); +	mFontBitmapCachep->init(max_char_width, max_char_height);  	if (!mFTFace->charmap)  	{ @@ -231,7 +262,7 @@ BOOL LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 v  	if (!mIsFallback)  	{  		// Add the default glyph -		addGlyphFromFont(this, 0, 0); +		addGlyphFromFont(this, 0, 0, EFontGlyphType::Grayscale);  	}  	mName = filename; @@ -323,14 +354,11 @@ void LLFontFreetype::clearFontStreams()  }  #endif -void LLFontFreetype::setFallbackFonts(const font_vector_t &font) -{ -	mFallbackFonts = font; -} - -const LLFontFreetype::font_vector_t &LLFontFreetype::getFallbackFonts() const +void LLFontFreetype::addFallbackFont(const LLPointer<LLFontFreetype>& fallback_font, const char_functor_t& functor)  { -	return mFallbackFonts; +	// Insert functor fallbacks before generic fallbacks +	mFallbackFonts.insert((functor) ? std::find_if(mFallbackFonts.begin(), mFallbackFonts.end(), [](const fallback_font_t& fe) { return !fe.second; }) : mFallbackFonts.end(), +	                      std::make_pair(fallback_font, functor));  }  F32 LLFontFreetype::getLineHeight() const @@ -354,7 +382,7 @@ F32 LLFontFreetype::getXAdvance(llwchar wch) const  		return 0.0;  	// Return existing info only if it is current -	LLFontGlyphInfo* gi = getGlyphInfo(wch); +	LLFontGlyphInfo* gi = getGlyphInfo(wch, EFontGlyphType::Unspecified);  	if (gi)  	{  		return gi->mXAdvance; @@ -386,10 +414,10 @@ F32 LLFontFreetype::getXKerning(llwchar char_left, llwchar char_right) const  		return 0.0;  	//llassert(!mIsFallback); -	LLFontGlyphInfo* left_glyph_info = getGlyphInfo(char_left);; +	LLFontGlyphInfo* left_glyph_info = getGlyphInfo(char_left, EFontGlyphType::Unspecified);;  	U32 left_glyph = left_glyph_info ? left_glyph_info->mGlyphIndex : 0;  	// Kern this puppy. -	LLFontGlyphInfo* right_glyph_info = getGlyphInfo(char_right); +	LLFontGlyphInfo* right_glyph_info = getGlyphInfo(char_right, EFontGlyphType::Unspecified);  	U32 right_glyph = right_glyph_info ? right_glyph_info->mGlyphIndex : 0;  	FT_Vector  delta; @@ -420,60 +448,91 @@ BOOL LLFontFreetype::hasGlyph(llwchar wch) const  	return(mCharGlyphInfoMap.find(wch) != mCharGlyphInfoMap.end());  } -LLFontGlyphInfo* LLFontFreetype::addGlyph(llwchar wch) const +LLFontGlyphInfo* LLFontFreetype::addGlyph(llwchar wch, EFontGlyphType glyph_type) const  {  	if (mFTFace == NULL)  		return FALSE;  	llassert(!mIsFallback); +	llassert(glyph_type < EFontGlyphType::Count);  	//LL_DEBUGS() << "Adding new glyph for " << wch << " to font" << LL_ENDL;  	FT_UInt glyph_index; +	// Fallback fonts with a functor have precedence over everything else +	fallback_font_vector_t::const_iterator it_fallback = mFallbackFonts.cbegin(); +	for (; it_fallback != mFallbackFonts.cend() && it_fallback->second; ++it_fallback) +	{ +		if (it_fallback->second(wch)) +		{ +			glyph_index = FT_Get_Char_Index(it_fallback->first->mFTFace, wch); +			if (glyph_index) +			{ +				return addGlyphFromFont(it_fallback->first, wch, glyph_index, glyph_type); +			} +		} +	} +  	// Initialize char to glyph map  	glyph_index = FT_Get_Char_Index(mFTFace, wch);  	if (glyph_index == 0)  	{  		//LL_INFOS() << "Trying to add glyph from fallback font!" << LL_ENDL; -		font_vector_t::const_iterator iter; -		for(iter = mFallbackFonts.begin(); iter != mFallbackFonts.end(); iter++) +		for (; it_fallback != mFallbackFonts.cend(); ++it_fallback)  		{ -			glyph_index = FT_Get_Char_Index((*iter)->mFTFace, wch); +			glyph_index = FT_Get_Char_Index(it_fallback->first->mFTFace, wch);  			if (glyph_index)  			{ -				return addGlyphFromFont(*iter, wch, glyph_index); +				return addGlyphFromFont(it_fallback->first, wch, glyph_index, glyph_type);  			}  		}  	} -	char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch); -	if (iter == mCharGlyphInfoMap.end()) +	std::pair<char_glyph_info_map_t::iterator, char_glyph_info_map_t::iterator> range_it = mCharGlyphInfoMap.equal_range(wch); +	char_glyph_info_map_t::iterator iter =  +		std::find_if(range_it.first, range_it.second, [&glyph_type](const char_glyph_info_map_t::value_type& entry) { return entry.second->mGlyphType == glyph_type; }); +	if (iter == range_it.second)  	{ -		return addGlyphFromFont(this, wch, glyph_index); +		return addGlyphFromFont(this, wch, glyph_index, glyph_type);  	}  	return NULL;  } -LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index) const +LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index, EFontGlyphType requested_glyph_type) const  {      LL_PROFILE_ZONE_SCOPED;  	if (mFTFace == NULL)  		return NULL;  	llassert(!mIsFallback); -	fontp->renderGlyph(glyph_index); +	fontp->renderGlyph(requested_glyph_type, glyph_index); + +	EFontGlyphType bitmap_glyph_type = EFontGlyphType::Unspecified; +	switch (fontp->mFTFace->glyph->bitmap.pixel_mode) +	{ +		case FT_PIXEL_MODE_MONO: +		case FT_PIXEL_MODE_GRAY: +			bitmap_glyph_type = EFontGlyphType::Grayscale; +			break; +		case FT_PIXEL_MODE_BGRA: +			bitmap_glyph_type = EFontGlyphType::Color; +			break; +		default: +			llassert_always(true); +			break; +	}  	S32 width = fontp->mFTFace->glyph->bitmap.width;  	S32 height = fontp->mFTFace->glyph->bitmap.rows;  	S32 pos_x, pos_y; -	S32 bitmap_num; -	mFontBitmapCachep->nextOpenPos(width, pos_x, pos_y, bitmap_num); +	U32 bitmap_num; +	mFontBitmapCachep->nextOpenPos(width, pos_x, pos_y, bitmap_glyph_type, bitmap_num);  	mAddGlyphCount++; -	LLFontGlyphInfo* gi = new LLFontGlyphInfo(glyph_index); +	LLFontGlyphInfo* gi = new LLFontGlyphInfo(glyph_index, requested_glyph_type);  	gi->mXBitmapOffset = pos_x;  	gi->mYBitmapOffset = pos_y; -	gi->mBitmapNum = bitmap_num; +	gi->mBitmapEntry = std::make_pair(bitmap_glyph_type, bitmap_num);  	gi->mWidth = width;  	gi->mHeight = height;  	gi->mXBearing = fontp->mFTFace->glyph->bitmap_left; @@ -484,8 +543,12 @@ LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, l  	insertGlyphInfo(wch, gi); -	llassert(fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO -	    || fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY); +	if (requested_glyph_type != bitmap_glyph_type) +	{ +		LLFontGlyphInfo* gi_temp = new LLFontGlyphInfo(*gi); +		gi_temp->mGlyphType = bitmap_glyph_type; +		insertGlyphInfo(wch, gi_temp); +	}  	if (fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO  	    || fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) @@ -520,78 +583,86 @@ LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, l  			buffer_row_stride = width;  		} -		switch (mFontBitmapCachep->getNumComponents()) -		{ -		case 1: -			mFontBitmapCachep->getImageRaw(bitmap_num)->setSubImage(pos_x, -																	pos_y, -																	width, -																	height, -																	buffer_data, -																	buffer_row_stride, -																	TRUE); -			break; -		case 2: -			setSubImageLuminanceAlpha(pos_x,	 -									  pos_y, -									  bitmap_num, -									  width, -									  height, -									  buffer_data, -									  buffer_row_stride); -			break; -		default: -			break; -		} +		setSubImageLuminanceAlpha(pos_x, +									pos_y, +									bitmap_num, +									width, +									height, +									buffer_data, +									buffer_row_stride);  		if (tmp_graydata)  			delete[] tmp_graydata; +	} +	else if (fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) +	{ +		setSubImageBGRA(pos_x, +		                pos_y, +		                bitmap_num, +		                fontp->mFTFace->glyph->bitmap.width, +		                fontp->mFTFace->glyph->bitmap.rows, +		                fontp->mFTFace->glyph->bitmap.buffer, +		                llabs(fontp->mFTFace->glyph->bitmap.pitch));  	} else { -		// we don't know how to handle this pixel format from FreeType; -		// omit it from the font-image. +		llassert(false);  	} -	LLImageGL *image_gl = mFontBitmapCachep->getImageGL(bitmap_num); -	LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_num); +	LLImageGL *image_gl = mFontBitmapCachep->getImageGL(bitmap_glyph_type, bitmap_num); +	LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_glyph_type, bitmap_num);  	image_gl->setSubImage(image_raw, 0, 0, image_gl->getWidth(), image_gl->getHeight());  	return gi;  } -LLFontGlyphInfo* LLFontFreetype::getGlyphInfo(llwchar wch) const +LLFontGlyphInfo* LLFontFreetype::getGlyphInfo(llwchar wch, EFontGlyphType glyph_type) const  { -	char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch); -	if (iter != mCharGlyphInfoMap.end()) +	std::pair<char_glyph_info_map_t::iterator, char_glyph_info_map_t::iterator> range_it = mCharGlyphInfoMap.equal_range(wch); + +	char_glyph_info_map_t::iterator iter = (EFontGlyphType::Unspecified != glyph_type) +		? std::find_if(range_it.first, range_it.second, [&glyph_type](const char_glyph_info_map_t::value_type& entry) { return entry.second->mGlyphType == glyph_type; }) +		: range_it.first; +	if (iter != range_it.second)  	{  		return iter->second;  	}  	else  	{  		// this glyph doesn't yet exist, so render it and return the result -		return addGlyph(wch); +		return addGlyph(wch, (EFontGlyphType::Unspecified != glyph_type) ? glyph_type : EFontGlyphType::Grayscale);  	}  }  void LLFontFreetype::insertGlyphInfo(llwchar wch, LLFontGlyphInfo* gi) const  { -	char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch); -	if (iter != mCharGlyphInfoMap.end()) +	llassert(gi->mGlyphType < EFontGlyphType::Count); +	std::pair<char_glyph_info_map_t::iterator, char_glyph_info_map_t::iterator> range_it = mCharGlyphInfoMap.equal_range(wch); + +	char_glyph_info_map_t::iterator iter = +		std::find_if(range_it.first, range_it.second, [&gi](const char_glyph_info_map_t::value_type& entry) { return entry.second->mGlyphType == gi->mGlyphType; }); +	if (iter != range_it.second)  	{  		delete iter->second;  		iter->second = gi;  	}  	else  	{ -		mCharGlyphInfoMap[wch] = gi; +		mCharGlyphInfoMap.insert(std::make_pair(wch, gi));  	}  } -void LLFontFreetype::renderGlyph(U32 glyph_index) const +void LLFontFreetype::renderGlyph(EFontGlyphType bitmap_type, U32 glyph_index) const  {  	if (mFTFace == NULL)  		return; -	llassert_always(! FT_Load_Glyph(mFTFace, glyph_index, FT_LOAD_FORCE_AUTOHINT) ); +	FT_Int32 load_flags = FT_LOAD_FORCE_AUTOHINT; +	if (EFontGlyphType::Color == bitmap_type) +	{ +		// We may not actually get a color render so our caller should always examine mFTFace->glyph->bitmap.pixel_mode +		load_flags |= FT_LOAD_COLOR; +	} + +	llassert_always(! FT_Load_Glyph(mFTFace, glyph_index, load_flags) );  	llassert_always(! FT_Render_Glyph(mFTFace->glyph, gFontRenderMode) ); @@ -601,7 +672,7 @@ void LLFontFreetype::renderGlyph(U32 glyph_index) const  void LLFontFreetype::reset(F32 vert_dpi, F32 horz_dpi)  {  	resetBitmapCache();  -	loadFace(mName, mPointSize, vert_dpi ,horz_dpi, mFontBitmapCachep->getNumComponents(), mIsFallback); +	loadFace(mName, mPointSize, vert_dpi ,horz_dpi, mIsFallback, 0);  	if (!mIsFallback)  	{  		// This is the head of the list - need to rebuild ourself and all fallbacks. @@ -611,11 +682,9 @@ void LLFontFreetype::reset(F32 vert_dpi, F32 horz_dpi)  		}  		else  		{ -			for(font_vector_t::iterator it = mFallbackFonts.begin(); -				it != mFallbackFonts.end(); -				++it) +			for (fallback_font_vector_t::iterator it = mFallbackFonts.begin(); it != mFallbackFonts.end(); ++it)  			{ -				(*it)->reset(vert_dpi, horz_dpi); +				it->first->reset(vert_dpi, horz_dpi);  			}  		}  	} @@ -637,7 +706,7 @@ void LLFontFreetype::resetBitmapCache()  	if(!mIsFallback)  	{  		// Add the empty glyph -		addGlyphFromFont(this, 0, 0); +		addGlyphFromFont(this, 0, 0, EFontGlyphType::Grayscale);  	}  } @@ -651,6 +720,34 @@ const std::string &LLFontFreetype::getName() const  	return mName;  } +static void dumpFontBitmap(const LLImageRaw* image_raw, const std::string& file_name) +{ +	LLPointer<LLImagePNG> tmpImage = new LLImagePNG(); +	if ( (tmpImage->encode(image_raw, 0.0f)) && (tmpImage->save(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, file_name))) ) +	{ +		LL_INFOS("Font") << "Successfully saved " << file_name << LL_ENDL; +	} +	else +	{ +		LL_WARNS("Font") << "Failed to save " << file_name << LL_ENDL; +	} +} + +void LLFontFreetype::dumpFontBitmaps() const +{ +	// Dump all the regular bitmaps (if any) +	for (int idx = 0, cnt = mFontBitmapCachep->getNumBitmaps(EFontGlyphType::Grayscale); idx < cnt; idx++) +	{ +		dumpFontBitmap(mFontBitmapCachep->getImageRaw(EFontGlyphType::Grayscale, idx), llformat("%s_%d_%d_%d.png", mFTFace->family_name, (int)(mPointSize * 10), mStyle, idx)); +	} + +	// Dump all the color bitmaps (if any) +	for (int idx = 0, cnt = mFontBitmapCachep->getNumBitmaps(EFontGlyphType::Color); idx < cnt; idx++) +	{ +		dumpFontBitmap(mFontBitmapCachep->getImageRaw(EFontGlyphType::Color, idx), llformat("%s_%d_%d_%d_clr.png", mFTFace->family_name, (int)(mPointSize * 10), mStyle, idx)); +	} +} +  const LLFontBitmapCache* LLFontFreetype::getFontBitmapCache() const  {  	return mFontBitmapCachep; @@ -666,9 +763,38 @@ U8 LLFontFreetype::getStyle() const  	return mStyle;  } +bool LLFontFreetype::setSubImageBGRA(U32 x, U32 y, U32 bitmap_num, U16 width, U16 height, const U8* data, U32 stride) const +{ +	LLImageRaw* image_raw = mFontBitmapCachep->getImageRaw(EFontGlyphType::Color, bitmap_num); +	llassert(!mIsFallback); +	llassert(image_raw && (image_raw->getComponents() == 4)); + +	// NOTE: inspired by LLImageRaw::setSubImage() +	U32* image_data = (U32*)image_raw->getData(); +	if (!image_data) +	{ +		return false; +	} + +	for (U32 idxRow = 0; idxRow < height; idxRow++) +	{ +		const U32 nSrcRow = height - 1 - idxRow; +		const U32 nSrcOffset = nSrcRow * width * image_raw->getComponents(); +		const U32 nDstOffset = (y + idxRow) * image_raw->getWidth() + x; + +		for (U32 idxCol = 0; idxCol < width; idxCol++) +		{ +			U32 nTemp = nSrcOffset + idxCol * 4; +			image_data[nDstOffset + idxCol] = data[nTemp + 3] << 24 | data[nTemp] << 16 | data[nTemp + 1] << 8 | data[nTemp + 2]; +		} +	} + +	return true; +} +  void LLFontFreetype::setSubImageLuminanceAlpha(U32 x, U32 y, U32 bitmap_num, U32 width, U32 height, U8 *data, S32 stride) const  { -	LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_num); +	LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(EFontGlyphType::Grayscale, bitmap_num);  	llassert(!mIsFallback);  	llassert(image_raw && (image_raw->getComponents() == 2)); diff --git a/indra/llrender/llfontfreetype.h b/indra/llrender/llfontfreetype.h index f61f169987..b036d337ba 100644 --- a/indra/llrender/llfontfreetype.h +++ b/indra/llrender/llfontfreetype.h @@ -56,9 +56,11 @@ private:  struct LLFontGlyphInfo  { -	LLFontGlyphInfo(U32 index); +	LLFontGlyphInfo(U32 index, EFontGlyphType glyph_type); +	LLFontGlyphInfo(const LLFontGlyphInfo& fgi);  	U32 mGlyphIndex; +	EFontGlyphType mGlyphType;  	// Metrics  	S32 mWidth;			// In pixels @@ -71,7 +73,7 @@ struct LLFontGlyphInfo  	S32 mYBitmapOffset; // Offset to the origin in the bitmap  	S32 mXBearing;	// Distance from baseline to left in pixels  	S32 mYBearing;	// Distance from baseline to top in pixels -	S32 mBitmapNum; // Which bitmap in the bitmap cache contains this glyph +	std::pair<EFontGlyphType, S32> mBitmapEntry; // Which bitmap in the bitmap cache contains this glyph  };  extern LLFontManager *gFontManagerp; @@ -84,7 +86,7 @@ public:  	// is_fallback should be true for fallback fonts that aren't used  	// to render directly (Unicode backup, primarily) -	BOOL loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, BOOL is_fallback, S32 face_n = 0); +	BOOL loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, bool is_fallback, S32 face_n);  	S32 getNumFaces(const std::string& filename); @@ -93,10 +95,8 @@ public:  	void clearFontStreams();  #endif -	typedef std::vector<LLPointer<LLFontFreetype> > font_vector_t; - -	void setFallbackFonts(const font_vector_t &font); -	const font_vector_t &getFallbackFonts() const; +	typedef std::function<bool(llwchar)> char_functor_t; +	void addFallbackFont(const LLPointer<LLFontFreetype>& fallback_font, const char_functor_t& functor = nullptr);  	// Global font metrics - in units of pixels  	F32 getLineHeight() const; @@ -135,7 +135,7 @@ public:  	F32 getXKerning(llwchar char_left, llwchar char_right) const; // Get the kerning between the two characters  	F32 getXKerning(const LLFontGlyphInfo* left_glyph_info, const LLFontGlyphInfo* right_glyph_info) const; // Get the kerning between the two characters -	LLFontGlyphInfo* getGlyphInfo(llwchar wch) const; +	LLFontGlyphInfo* getGlyphInfo(llwchar wch, EFontGlyphType glyph_type) const;  	void reset(F32 vert_dpi, F32 horz_dpi); @@ -143,6 +143,7 @@ public:  	const std::string& getName() const; +	void       dumpFontBitmaps() const;  	const LLFontBitmapCache* getFontBitmapCache() const;  	void setStyle(U8 style); @@ -151,10 +152,11 @@ public:  private:  	void resetBitmapCache();  	void setSubImageLuminanceAlpha(U32 x, U32 y, U32 bitmap_num, U32 width, U32 height, U8 *data, S32 stride = 0) const; +	bool setSubImageBGRA(U32 x, U32 y, U32 bitmap_num, U16 width, U16 height, const U8* data, U32 stride) const;  	BOOL hasGlyph(llwchar wch) const;		// Has a glyph for this character -	LLFontGlyphInfo* addGlyph(llwchar wch) const;		// Add a new character to the font if necessary -	LLFontGlyphInfo* addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index) const;	// Add a glyph from this font to the other (returns the glyph_index, 0 if not found) -	void renderGlyph(U32 glyph_index) const; +	LLFontGlyphInfo* addGlyph(llwchar wch, EFontGlyphType glyph_type) const;		// Add a new character to the font if necessary +	LLFontGlyphInfo* addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index, EFontGlyphType bitmap_type) const;	// Add a glyph from this font to the other (returns the glyph_index, 0 if not found) +	void renderGlyph(EFontGlyphType bitmap_type, U32 glyph_index) const;  	void insertGlyphInfo(llwchar wch, LLFontGlyphInfo* gi) const;  	std::string mName; @@ -174,9 +176,12 @@ private:  #endif  	BOOL mIsFallback; -	font_vector_t mFallbackFonts; // A list of fallback fonts to look for glyphs in (for Unicode chars) +	typedef std::pair<LLPointer<LLFontFreetype>, char_functor_t> fallback_font_t; +	typedef std::vector<fallback_font_t> fallback_font_vector_t; +	fallback_font_vector_t mFallbackFonts; // A list of fallback fonts to look for glyphs in (for Unicode chars) -	typedef boost::unordered_map<llwchar, LLFontGlyphInfo*> char_glyph_info_map_t; +	// *NOTE: the same glyph can be present with multiple representations (but the pointer is always unique) +	typedef boost::unordered_multimap<llwchar, LLFontGlyphInfo*> char_glyph_info_map_t;  	mutable char_glyph_info_map_t mCharGlyphInfoMap; // Information about glyph location in bitmap  	mutable LLFontBitmapCache* mFontBitmapCachep; diff --git a/indra/llrender/llfontfreetypesvg.cpp b/indra/llrender/llfontfreetypesvg.cpp new file mode 100644 index 0000000000..19d327a4c9 --- /dev/null +++ b/indra/llrender/llfontfreetypesvg.cpp @@ -0,0 +1,205 @@ +/** + * @file llfontfreetypesvg.cpp + * @brief Freetype font library SVG glyph rendering + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llfontfreetypesvg.h" + +#if LL_WINDOWS +#pragma warning (push) +#pragma warning (disable : 4702) +#endif + +#define NANOSVG_IMPLEMENTATION +#include <nanosvg/nanosvg.h> +#define NANOSVGRAST_IMPLEMENTATION +#include <nanosvg/nanosvgrast.h> + +#if LL_WINDOWS +#pragma warning (pop) +#endif + +struct LLSvgRenderData +{ +	FT_UInt    GlyphIndex = 0; +	FT_Error   Error = FT_Err_Ok; // FreeType currently (@2.12.1) ignores the error value returned by the preset glyph slot callback so we return it at render time +	// (See https://github.com/freetype/freetype/blob/5faa1df8b93ebecf0f8fd5fe8fda7b9082eddced/src/base/ftobjs.c#L1170) +	NSVGimage* pNSvgImage = nullptr; +	float      Scale = 0.f; +}; + +// static +FT_Error LLFontFreeTypeSvgRenderer::OnInit(FT_Pointer* state) +{ +	// The SVG driver hook state is shared across all callback invocations; since our state is lightweight +	// we store it in the glyph instead. +	*state = nullptr; + +	return FT_Err_Ok; +} + +// static +void LLFontFreeTypeSvgRenderer::OnFree(FT_Pointer* state) +{ +} + +// static +void LLFontFreeTypeSvgRenderer::OnDataFinalizer(void* objectp) +{ +	FT_GlyphSlot glyph_slot = static_cast<FT_GlyphSlot>(objectp); + +	LLSvgRenderData* pData = static_cast<LLSvgRenderData*>(glyph_slot->generic.data); +	glyph_slot->generic.data = nullptr; +	glyph_slot->generic.finalizer = nullptr; +	delete(pData); +} + +//static +FT_Error LLFontFreeTypeSvgRenderer::OnPresetGlypthSlot(FT_GlyphSlot glyph_slot, FT_Bool cache, FT_Pointer*) +{ +	FT_SVG_Document document = static_cast<FT_SVG_Document>(glyph_slot->other); + +	llassert(!glyph_slot->generic.data || !cache || glyph_slot->glyph_index == ((LLSvgRenderData*)glyph_slot->generic.data)->GlyphIndex); +	if (!glyph_slot->generic.data) +	{ +		glyph_slot->generic.data = new LLSvgRenderData(); +		glyph_slot->generic.finalizer = LLFontFreeTypeSvgRenderer::OnDataFinalizer; +	} +	LLSvgRenderData* datap = static_cast<LLSvgRenderData*>(glyph_slot->generic.data); +	if (!cache) +	{ +		datap->GlyphIndex = glyph_slot->glyph_index; +		datap->Error = FT_Err_Ok; +	} + +	// NOTE: nsvgParse modifies the input string so we need a temporary copy +	llassert(!datap->pNSvgImage || cache); +	if (!datap->pNSvgImage) +	{ +		char* document_buffer = new char[document->svg_document_length + 1]; +		memcpy(document_buffer, document->svg_document, document->svg_document_length); +		document_buffer[document->svg_document_length] = '\0'; + +		datap->pNSvgImage = nsvgParse(document_buffer, "px", 0.); + +		delete[] document_buffer; +	} + +	if (!datap->pNSvgImage) +	{ +		datap->Error = FT_Err_Invalid_SVG_Document; +		return FT_Err_Invalid_SVG_Document; +	} + +	// We don't (currently) support transformations so test for an identity rotation matrix + zero translation +	if (document->transform.xx != 1 << 16 || document->transform.yx != 0 || +		document->transform.xy != 0 || document->transform.yy != 1 << 16 || +		document->delta.x > 0 || document->delta.y > 0) +	{ +		datap->Error = FT_Err_Unimplemented_Feature; +		return FT_Err_Unimplemented_Feature; +	} + +	float svg_width = datap->pNSvgImage->width; +	float svg_height = datap->pNSvgImage->height; +	if (svg_width == 0.f || svg_height == 0.f) +	{ +		svg_width = document->units_per_EM; +		svg_height = document->units_per_EM; +	} + +	float svg_x_scale = (float)document->metrics.x_ppem / floorf(svg_width); +	float svg_y_scale = (float)document->metrics.y_ppem / floorf(svg_height); +	float svg_scale = llmin(svg_x_scale, svg_y_scale); +	datap->Scale = svg_scale; + +	glyph_slot->bitmap.width = floorf(svg_width) * svg_scale; +	glyph_slot->bitmap.rows = floorf(svg_height) * svg_scale; +	glyph_slot->bitmap_left = (document->metrics.x_ppem - glyph_slot->bitmap.width) / 2; +	glyph_slot->bitmap_top = glyph_slot->face->size->metrics.ascender / 64.f; +	glyph_slot->bitmap.pitch = glyph_slot->bitmap.width * 4; +	glyph_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; + +	/* Copied as-is from fcft (MIT license) */ + +	// Compute all the bearings and set them correctly. The outline is scaled already, we just need to use the bounding box. +	float horiBearingX = 0.; +	float horiBearingY = -glyph_slot->bitmap_top; + +	// XXX parentheses correct? +	float vertBearingX = glyph_slot->metrics.horiBearingX / 64.0f - glyph_slot->metrics.horiAdvance / 64.0f / 2; +	float vertBearingY = (glyph_slot->metrics.vertAdvance / 64.0f - glyph_slot->metrics.height / 64.0f) / 2; + +	// Do conversion in two steps to avoid 'bad function cast' warning +	glyph_slot->metrics.width = glyph_slot->bitmap.width * 64; +	glyph_slot->metrics.height = glyph_slot->bitmap.rows * 64; +	glyph_slot->metrics.horiBearingX = horiBearingX * 64; +	glyph_slot->metrics.horiBearingY = horiBearingY * 64; +	glyph_slot->metrics.vertBearingX = vertBearingX * 64; +	glyph_slot->metrics.vertBearingY = vertBearingY * 64; +	if (glyph_slot->metrics.vertAdvance == 0) +	{ +		glyph_slot->metrics.vertAdvance = glyph_slot->bitmap.rows * 1.2f * 64; +	} + +	return FT_Err_Ok; +} + +// static +FT_Error LLFontFreeTypeSvgRenderer::OnRender(FT_GlyphSlot glyph_slot, FT_Pointer*) +{ +	LLSvgRenderData* datap = static_cast<LLSvgRenderData*>(glyph_slot->generic.data); +	llassert(FT_Err_Ok == datap->Error); +	if (FT_Err_Ok != datap->Error) +	{ +		return datap->Error; +	} + +	// Render to glyph bitmap +	NSVGrasterizer* nsvgRasterizer = nsvgCreateRasterizer(); +	nsvgRasterize(nsvgRasterizer, datap->pNSvgImage, 0, 0, datap->Scale, glyph_slot->bitmap.buffer, glyph_slot->bitmap.width, glyph_slot->bitmap.rows, glyph_slot->bitmap.pitch); +	nsvgDeleteRasterizer(nsvgRasterizer); +	nsvgDelete(datap->pNSvgImage); +	datap->pNSvgImage = nullptr; + +	// Convert from RGBA to BGRA +	U32* pixel_buffer = (U32*)glyph_slot->bitmap.buffer; U8* byte_buffer = glyph_slot->bitmap.buffer; +	for (size_t y = 0, h = glyph_slot->bitmap.rows; y < h; y++) +	{ +		for (size_t x = 0, w = glyph_slot->bitmap.pitch / 4; x < w; x++) +		{ +			size_t pixel_idx = y * w + x; +			size_t byte_idx = pixel_idx * 4; +			U8 alpha = byte_buffer[byte_idx + 3]; +			// Store as ARGB (*TODO - do we still have to care about endianness?) +			pixel_buffer[y * w + x] = alpha << 24 | (byte_buffer[byte_idx] * alpha / 0xFF) << 16 | (byte_buffer[byte_idx + 1] * alpha / 0xFF) << 8 | (byte_buffer[byte_idx + 2] * alpha / 0xFF); +		} +	} + +	glyph_slot->format = FT_GLYPH_FORMAT_BITMAP; +	glyph_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; +	return FT_Err_Ok; +} diff --git a/indra/llrender/llfontfreetypesvg.h b/indra/llrender/llfontfreetypesvg.h new file mode 100644 index 0000000000..b5f541991a --- /dev/null +++ b/indra/llrender/llfontfreetypesvg.h @@ -0,0 +1,54 @@ +/** + * @file llfontfreetypesvg.h + * @brief Freetype font library SVG glyph rendering + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#pragma once + +#include <ft2build.h> +#include FT_TYPES_H +#include FT_MODULE_H +#include FT_OTSVG_H + + // See https://freetype.org/freetype2/docs/reference/ft2-svg_fonts.html +class LLFontFreeTypeSvgRenderer +{ +public: +	// Called when the very first OT-SVG glyph is rendered (across the entire lifetime of our FT_Library object) +	static FT_Error OnInit(FT_Pointer* state); + +	// Called when the ot-svg module is being freed (but only called if the init hook was called previously) +	static void     OnFree(FT_Pointer* state); + +	// Called to preset the glyph slot, twice per glyph: +	//   - when FT_Load_Glyph needs to preset the glyph slot (with cache == false) +	//   - right before the svg module calls the render callback hook. (with cache == true) +	static FT_Error OnPresetGlypthSlot(FT_GlyphSlot glyph_slot, FT_Bool cache, FT_Pointer* state); + +	// Called to render an OT-SVG glyph (right after the preset hook OnPresetGlypthSlot was called with cache set to TRUE) +	static FT_Error OnRender(FT_GlyphSlot glyph_slot, FT_Pointer* state); + +	// Called to deallocate our per glyph slot data +	static void OnDataFinalizer(void* objectp); +}; diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 1bf061bc8d..001b7fd7b8 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -89,14 +89,14 @@ void LLFontGL::destroyGL()  	mFontFreetype->destroyGL();  } -BOOL LLFontGL::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, BOOL is_fallback, S32 face_n) +BOOL LLFontGL::loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, bool is_fallback, S32 face_n)  {  	if(mFontFreetype == reinterpret_cast<LLFontFreetype*>(NULL))  	{  		mFontFreetype = new LLFontFreetype;  	} -	return mFontFreetype->loadFace(filename, point_size, vert_dpi, horz_dpi, components, is_fallback, face_n); +	return mFontFreetype->loadFace(filename, point_size, vert_dpi, horz_dpi, is_fallback, face_n);  }  S32 LLFontGL::getNumFaces(const std::string& filename) @@ -110,14 +110,14 @@ S32 LLFontGL::getNumFaces(const std::string& filename)  }  S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRect& rect, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, -    ShadowType shadow, S32 max_chars, F32* right_x, BOOL use_ellipses) const +    ShadowType shadow, S32 max_chars, F32* right_x, BOOL use_ellipses, BOOL use_color) const  {      LLRectf rect_float(rect.mLeft, rect.mTop, rect.mRight, rect.mBottom); -    return render(wstr, begin_offset, rect_float, color, halign, valign, style, shadow, max_chars, right_x, use_ellipses); +    return render(wstr, begin_offset, rect_float, color, halign, valign, style, shadow, max_chars, right_x, use_ellipses, use_color);  }  S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRectf& rect, const LLColor4 &color, HAlign halign, VAlign valign, U8 style,  -					 ShadowType shadow, S32 max_chars, F32* right_x, BOOL use_ellipses) const +					 ShadowType shadow, S32 max_chars, F32* right_x, BOOL use_ellipses, BOOL use_color) const  {  	F32 x = rect.mLeft;  	F32 y = 0.f; @@ -138,12 +138,12 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRectf& rec  		y = rect.mBottom;  		break;  	} -	return render(wstr, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, rect.getWidth(), right_x, use_ellipses); +	return render(wstr, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, rect.getWidth(), right_x, use_ellipses, use_color);  }  S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style,  -					 ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses) const +					 ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses, BOOL use_color) const  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; @@ -278,7 +278,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  	LLColor4U text_color(color); -	S32 bitmap_num = -1; +	std::pair<EFontGlyphType, S32> bitmap_entry = std::make_pair(EFontGlyphType::Grayscale, -1);  	S32 glyph_count = 0;  	for (i = begin_offset; i < begin_offset + length; i++)  	{ @@ -288,7 +288,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  		next_glyph = NULL;  		if(!fgi)  		{ -			fgi = mFontFreetype->getGlyphInfo(wch); +			fgi = mFontFreetype->getGlyphInfo(wch, (!use_color) ? EFontGlyphType::Grayscale : EFontGlyphType::Color);  		}  		if (!fgi)  		{ @@ -296,8 +296,8 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  			break;  		}  		// Per-glyph bitmap texture. -		S32 next_bitmap_num = fgi->mBitmapNum; -		if (next_bitmap_num != bitmap_num) +		std::pair<EFontGlyphType, S32> next_bitmap_entry = fgi->mBitmapEntry; +		if (next_bitmap_entry != bitmap_entry)  		{  			// Actually draw the queued glyphs before switching their texture;  			// otherwise the queued glyphs will be taken from wrong textures. @@ -311,8 +311,8 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  				glyph_count = 0;  			} -			bitmap_num = next_bitmap_num; -			LLImageGL *font_image = font_bitmap_cache->getImageGL(bitmap_num); +			bitmap_entry = next_bitmap_entry; +			LLImageGL* font_image = font_bitmap_cache->getImageGL(bitmap_entry.first, bitmap_entry.second);  			gGL.getTexUnit(0)->bind(font_image);  		} @@ -345,7 +345,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  			glyph_count = 0;  		} -		drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, text_color, style_to_add, shadow, drop_shadow_strength); +		drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, (bitmap_entry.first == EFontGlyphType::Grayscale) ? text_color : LLColor4U::white, style_to_add, shadow, drop_shadow_strength);  		chars_drawn++;  		cur_x += fgi->mXAdvance; @@ -355,7 +355,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  		if (next_char && (next_char < LAST_CHARACTER))  		{  			// Kern this puppy. -			next_glyph = mFontFreetype->getGlyphInfo(next_char); +			next_glyph = mFontFreetype->getGlyphInfo(next_char, (!use_color) ? EFontGlyphType::Grayscale : EFontGlyphType::Color);  			cur_x += mFontFreetype->getXKerning(fgi, next_glyph);  		} @@ -409,7 +409,8 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  				shadow,  				S32_MAX, max_pixels,  				right_x, -				FALSE);  +				FALSE, +				use_color);   		gGL.popUIMatrix();  	} @@ -423,19 +424,19 @@ S32 LLFontGL::render(const LLWString &text, S32 begin_offset, F32 x, F32 y, cons  	return render(text, begin_offset, x, y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE);  } -S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign,  VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels,  F32* right_x, BOOL use_ellipses) const +S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign,  VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels,  F32* right_x, BOOL use_ellipses, BOOL use_color) const  { -	return render(utf8str_to_wstring(text), begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses); +	return render(utf8str_to_wstring(text), begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color);  }  S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color) const  { -	return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE); +	return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE, FALSE);  }  S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow) const  { -	return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, halign, valign, style, shadow, S32_MAX, S32_MAX, NULL, FALSE); +	return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, halign, valign, style, shadow, S32_MAX, S32_MAX, NULL, FALSE, FALSE);  }  // font metrics - override for LLFontFreetype that returns units of virtual pixels @@ -512,7 +513,7 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars  		next_glyph = NULL;  		if(!fgi)  		{ -			fgi = mFontFreetype->getGlyphInfo(wch); +			fgi = mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);  		}  		F32 advance = mFontFreetype->getXAdvance(fgi); @@ -532,7 +533,7 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars  			&& (next_char < LAST_CHARACTER))  		{  			// Kern this puppy. -			next_glyph = mFontFreetype->getGlyphInfo(next_char); +			next_glyph = mFontFreetype->getGlyphInfo(next_char, EFontGlyphType::Unspecified);  			cur_x += mFontFreetype->getXKerning(fgi, next_glyph);  		}  		// Round after kerning. @@ -550,7 +551,7 @@ void LLFontGL::generateASCIIglyphs()      LL_PROFILE_ZONE_SCOPED_CATEGORY_UI      for (U32 i = 32; (i < 127); i++)      { -        mFontFreetype->getGlyphInfo(i); +        mFontFreetype->getGlyphInfo(i, EFontGlyphType::Grayscale);      }  } @@ -624,7 +625,7 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch  		next_glyph = NULL;  		if(!fgi)  		{ -			fgi = mFontFreetype->getGlyphInfo(wch); +			fgi = mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);  			if (NULL == fgi)  			{ @@ -649,7 +650,7 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch  		if (((i+1) < max_chars) && wchars[i+1])  		{  			// Kern this puppy. -			next_glyph = mFontFreetype->getGlyphInfo(wchars[i+1]); +			next_glyph = mFontFreetype->getGlyphInfo(wchars[i+1], EFontGlyphType::Unspecified);  			cur_x += mFontFreetype->getXKerning(fgi, next_glyph);  		} @@ -696,7 +697,7 @@ S32	LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_  	{  		llwchar wch = wchars[i]; -		const LLFontGlyphInfo* fgi= mFontFreetype->getGlyphInfo(wch); +		const LLFontGlyphInfo* fgi= mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);  		// last character uses character width, since the whole character needs to be visible  		// other characters just use advance @@ -771,7 +772,7 @@ S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, S32 begin_offset, F32 t  		next_glyph = NULL;  		if(!glyph)  		{ -			glyph = mFontFreetype->getGlyphInfo(wch); +			glyph = mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);  		}  		F32 char_width = mFontFreetype->getXAdvance(glyph); @@ -801,7 +802,7 @@ S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, S32 begin_offset, F32 t  			&& (wchars[(pos + 1)]))  		{  			// Kern this puppy. -			next_glyph = mFontFreetype->getGlyphInfo(wchars[pos + 1]); +			next_glyph = mFontFreetype->getGlyphInfo(wchars[pos + 1], EFontGlyphType::Unspecified);  			cur_x += mFontFreetype->getXKerning(glyph, next_glyph);  		} @@ -841,6 +842,26 @@ void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::st  	LLFontGL::loadDefaultFonts();  } +void LLFontGL::dumpTextures() +{ +	if (mFontFreetype.notNull()) +	{ +		mFontFreetype->dumpFontBitmaps(); +	} +} + +// static +void LLFontGL::dumpFonts() +{ +	sFontRegistry->dump(); +} + +// static +void LLFontGL::dumpFontTextures() +{ +	sFontRegistry->dumpTextures(); +} +  // Force standard fonts to get generated up front.  // This is primarily for error detection purposes.  // Don't do this during initClass because it can be slow and we want to get @@ -1003,6 +1024,13 @@ LLFontGL::VAlign LLFontGL::vAlignFromName(const std::string& name)  	return gl_vfont_align;  } + //static +LLFontGL* LLFontGL::getFontEmoji() +{ +	static LLFontGL* fontp = getFont(LLFontDescriptor("Emoji", "Large", 0)); +	return fontp;; +} +  //static  LLFontGL* LLFontGL::getFontMonospace()  { diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index 3b58a37d33..915c2439a3 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -87,7 +87,7 @@ public:  	void destroyGL(); -	BOOL loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, const S32 components, BOOL is_fallback, S32 face_n = 0); +	BOOL loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, bool is_fallback, S32 face_n);  	S32 getNumFaces(const std::string& filename); @@ -98,7 +98,8 @@ public:  				U8 style = NORMAL, ShadowType shadow = NO_SHADOW,   				S32 max_chars = S32_MAX,  				F32* right_x=NULL,  -				BOOL use_ellipses = FALSE) const; +				BOOL use_ellipses = FALSE, +				BOOL use_color = FALSE) const;  	S32 render(const LLWString &text, S32 begin_offset,   				const LLRectf& rect,  @@ -107,7 +108,8 @@ public:  				U8 style = NORMAL, ShadowType shadow = NO_SHADOW,   				S32 max_chars = S32_MAX,  				F32* right_x=NULL,  -				BOOL use_ellipses = FALSE) const; +				BOOL use_ellipses = FALSE, +				BOOL use_color = FALSE) const;  	S32 render(const LLWString &text, S32 begin_offset,   				F32 x, F32 y,  @@ -116,12 +118,13 @@ public:  				U8 style = NORMAL, ShadowType shadow = NO_SHADOW,   				S32 max_chars = S32_MAX, S32 max_pixels = S32_MAX,   				F32* right_x=NULL,  -				BOOL use_ellipses = FALSE) const; +				BOOL use_ellipses = FALSE, +				BOOL use_color = FALSE) const;  	S32 render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color) const;  	// renderUTF8 does a conversion, so is slower! -	S32 renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign,  VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels,  F32* right_x, BOOL use_ellipses) const; +	S32 renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign,  VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels,  F32* right_x, BOOL use_ellipses, BOOL use_color) const;  	S32 renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color) const;  	S32 renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style = NORMAL, ShadowType shadow = NO_SHADOW) const; @@ -165,6 +168,10 @@ public:  	static void initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, bool create_gl_textures = true); +	       void dumpTextures(); +	static void dumpFonts(); +	static void dumpFontTextures(); +  	// Load sans-serif, sans-serif-small, etc.  	// Slow, requires multiple seconds to load fonts.  	static bool loadDefaultFonts(); @@ -187,6 +194,7 @@ public:  	static void setFontDisplay(BOOL flag) { sDisplayFont = flag; } +	static LLFontGL* getFontEmoji();  	static LLFontGL* getFontMonospace();  	static LLFontGL* getFontSansSerifSmall();  	static LLFontGL* getFontSansSerif(); diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp index bc1a2f8887..44f2bd4cc5 100644 --- a/indra/llrender/llfontregistry.cpp +++ b/indra/llrender/llfontregistry.cpp @@ -47,6 +47,10 @@ bool init_from_xml(LLFontRegistry* registry, LLXMLNodePtr node);  const std::string MACOSX_FONT_PATH_LIBRARY = "/Library/Fonts/";  const std::string MACOSX_FONT_SUPPLEMENTAL = "Supplemental/"; +LLFontDescriptor::char_functor_map_t LLFontDescriptor::mCharFunctors({ +	{ "is_emoji", LLStringOps::isEmoji } +}); +  LLFontDescriptor::LLFontDescriptor():  	mStyle(0)  { @@ -55,22 +59,22 @@ LLFontDescriptor::LLFontDescriptor():  LLFontDescriptor::LLFontDescriptor(const std::string& name,  								   const std::string& size,   								   const U8 style, -								   const string_vec_t& file_names): +								   const font_file_info_vec_t& font_files):  	mName(name),  	mSize(size),  	mStyle(style), -	mFileNames(file_names) +	mFontFiles(font_files)  {  }  LLFontDescriptor::LLFontDescriptor(const std::string& name,  	const std::string& size,  	const U8 style, -	const string_vec_t& file_names, -	const string_vec_t& ft_collection_listections) : -	LLFontDescriptor(name, size, style, file_names) +	const font_file_info_vec_t& font_list, +	const font_file_info_vec_t& font_collection_files) : +	LLFontDescriptor(name, size, style, font_list)  { -	mFontCollectionsList = ft_collection_listections; +	mFontCollectionFiles = font_collection_files;  }  LLFontDescriptor::LLFontDescriptor(const std::string& name, @@ -82,7 +86,6 @@ LLFontDescriptor::LLFontDescriptor(const std::string& name,  {  } -  bool LLFontDescriptor::operator<(const LLFontDescriptor& b) const  {  	if (mName < b.mName) @@ -175,7 +178,19 @@ LLFontDescriptor LLFontDescriptor::normalize() const  	if (removeSubString(new_name,"Italic"))  		new_style |= LLFontGL::ITALIC; -	return LLFontDescriptor(new_name,new_size,new_style,getFileNames(),getFontCollectionsList()); +	return LLFontDescriptor(new_name,new_size,new_style, getFontFiles(), getFontCollectionFiles()); +} + +void LLFontDescriptor::addFontFile(const std::string& file_name, const std::string& char_functor) +{ +	char_functor_map_t::const_iterator it = mCharFunctors.find(char_functor); +	mFontFiles.push_back(LLFontFileInfo(file_name, (mCharFunctors.end() != it) ? it->second : nullptr)); +} + +void LLFontDescriptor::addFontCollectionFile(const std::string& file_name, const std::string& char_functor) +{ +	char_functor_map_t::const_iterator it = mCharFunctors.find(char_functor); +	mFontCollectionFiles.push_back(LLFontFileInfo(file_name, (mCharFunctors.end() != it) ? it->second : nullptr));  }  LLFontRegistry::LLFontRegistry(bool create_gl_textures) @@ -273,17 +288,24 @@ bool font_desc_init_from_xml(LLXMLNodePtr node, LLFontDescriptor& desc)  		if (child->hasName("file"))  		{  			std::string font_file_name = child->getTextContents(); -			desc.getFileNames().push_back(font_file_name); -			 +			std::string char_functor; + +			if (child->hasAttribute("functor")) +			{ +				child->getAttributeString("functor", char_functor); +			} +  			if (child->hasAttribute("load_collection"))  			{  				BOOL col = FALSE;  				child->getAttributeBOOL("load_collection", col);  				if (col)  				{ -					desc.getFontCollectionsList().push_back(font_file_name); +					desc.addFontCollectionFile(font_file_name, char_functor);  				}  			} + +			desc.addFontFile(font_file_name, char_functor);  		}  		else if (child->hasName("os"))  		{ @@ -326,19 +348,19 @@ bool init_from_xml(LLFontRegistry* registry, LLXMLNodePtr node)  					// A little roundabout because the map key is const,  					// so we have to fetch it, make a new map key, and  					// replace the old entry. -					string_vec_t match_file_names = match_desc->getFileNames(); -					match_file_names.insert(match_file_names.begin(), -											desc.getFileNames().begin(), -											desc.getFileNames().end()); +					font_file_info_vec_t font_files = match_desc->getFontFiles(); +					font_files.insert(font_files.begin(), +									  desc.getFontFiles().begin(), +									  desc.getFontFiles().end()); -					string_vec_t collections_list = match_desc->getFontCollectionsList(); -					collections_list.insert(collections_list.begin(), -						desc.getFontCollectionsList().begin(), -						desc.getFontCollectionsList().end()); +					font_file_info_vec_t font_collection_files = match_desc->getFontCollectionFiles(); +					font_collection_files.insert(font_collection_files.begin(), +						desc.getFontCollectionFiles().begin(), +						desc.getFontCollectionFiles().end());  					LLFontDescriptor new_desc = *match_desc; -					new_desc.getFileNames() = match_file_names; -					new_desc.getFontCollectionsList() = collections_list; +					new_desc.setFontFiles(font_files); +					new_desc.setFontCollectionFiles(font_collection_files);  					registry->mFontMap.erase(*match_desc);  					registry->mFontMap[new_desc] = NULL;  				} @@ -423,82 +445,80 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc)  	// Build list of font names to look for.  	// Files specified for this font come first, followed by those from the default descriptor. -	string_vec_t file_names = match_desc->getFileNames(); -	string_vec_t ft_collection_list = match_desc->getFontCollectionsList(); -	string_vec_t default_file_names; +	font_file_info_vec_t font_files = match_desc->getFontFiles(); +	font_file_info_vec_t font_collection_files = match_desc->getFontCollectionFiles();  	LLFontDescriptor default_desc("default",s_template_string,0);  	const LLFontDescriptor *match_default_desc = getMatchingFontDesc(default_desc);  	if (match_default_desc)  	{ -		file_names.insert(file_names.end(), -						  match_default_desc->getFileNames().begin(), -						  match_default_desc->getFileNames().end()); -		ft_collection_list.insert(ft_collection_list.end(), -			match_default_desc->getFontCollectionsList().begin(), -			match_default_desc->getFontCollectionsList().end()); +		font_files.insert(font_files.end(), +						  match_default_desc->getFontFiles().begin(), +						  match_default_desc->getFontFiles().end()); +		font_collection_files.insert(font_collection_files.end(), +			match_default_desc->getFontCollectionFiles().begin(), +			match_default_desc->getFontCollectionFiles().end());  	}  	// Add ultimate fallback list - generated dynamically on linux,  	// null elsewhere. -	file_names.insert(file_names.end(), -					  getUltimateFallbackList().begin(), -					  getUltimateFallbackList().end()); +	std::transform(getUltimateFallbackList().begin(), getUltimateFallbackList().end(), std::back_inserter(font_files), +	               [](const std::string& file_name) { return LLFontFileInfo(file_name); });  	// Load fonts based on names. -	if (file_names.empty()) +	if (font_files.empty())  	{  		LL_WARNS() << "createFont failed, no file names specified" << LL_ENDL;  		return NULL;  	} -	LLFontFreetype::font_vector_t fontlist;  	LLFontGL *result = NULL; -	// Snarf all fonts we can into fontlist.  First will get pulled -	// off the list and become the "head" font, set to non-fallback. +	// The first font will get pulled will be the "head" font, set to non-fallback.  	// Rest will consitute the fallback list.  	BOOL is_first_found = TRUE; -	std::string local_path = LLFontGL::getFontPathLocal(); -	std::string sys_path = LLFontGL::getFontPathSystem(); -	 +	string_vec_t font_search_paths; +	font_search_paths.push_back(LLFontGL::getFontPathLocal()); +	font_search_paths.push_back(LLFontGL::getFontPathSystem()); +#if LL_DARWIN +	font_search_paths.push_back(MACOSX_FONT_PATH_LIBRARY); +	font_search_paths.push_back(MACOSX_FONT_PATH_LIBRARY + MACOSX_FONT_SUPPLEMENTAL); +	font_search_paths.push_back(sys_path + MACOSX_FONT_SUPPLEMENTAL); +#endif +  	// The fontname string may contain multiple font file names separated by semicolons.  	// Break it apart and try loading each one, in order. -	for(string_vec_t::iterator file_name_it = file_names.begin(); -		file_name_it != file_names.end();  -		++file_name_it) +	for(font_file_info_vec_t::iterator font_file_it = font_files.begin(); +		font_file_it != font_files.end(); +		++font_file_it)  	{  		LLFontGL *fontp = NULL; -		string_vec_t font_paths; -		font_paths.push_back(local_path + *file_name_it); -		font_paths.push_back(sys_path + *file_name_it); -#if LL_DARWIN -		font_paths.push_back(MACOSX_FONT_PATH_LIBRARY + *file_name_it); -		font_paths.push_back(MACOSX_FONT_PATH_LIBRARY + MACOSX_FONT_SUPPLEMENTAL + *file_name_it); -		font_paths.push_back(sys_path +  MACOSX_FONT_SUPPLEMENTAL + *file_name_it); -#endif -		 -		bool is_ft_collection = (std::find(ft_collection_list.begin(), ft_collection_list.end(), *file_name_it) != ft_collection_list.end()); + +		bool is_ft_collection = (std::find_if(font_collection_files.begin(), font_collection_files.end(), +		                                      [&font_file_it](const LLFontFileInfo& ffi) { return font_file_it->FileName == ffi.FileName; }) != font_collection_files.end()); +  		// *HACK: Fallback fonts don't render, so we can use that to suppress  		// creation of OpenGL textures for test apps. JC  		BOOL is_fallback = !is_first_found || !mCreateGLTextures;  		F32 extra_scale = (is_fallback)?fallback_scale:1.0;  		F32 point_size_scale = extra_scale * point_size;  		bool is_font_loaded = false; -		for(string_vec_t::iterator font_paths_it = font_paths.begin(); -			font_paths_it != font_paths.end(); -			++font_paths_it) +		for(string_vec_t::iterator font_search_path_it = font_search_paths.begin(); +			font_search_path_it != font_search_paths.end(); +			++font_search_path_it)  		{ +			const std::string font_path = *font_search_path_it + font_file_it->FileName; +  			fontp = new LLFontGL; -			S32 num_faces = is_ft_collection ? fontp->getNumFaces(*font_paths_it) : 1; +			S32 num_faces = is_ft_collection ? fontp->getNumFaces(font_path) : 1;  			for (S32 i = 0; i < num_faces; i++)  			{  				if (fontp == NULL)  				{  					fontp = new LLFontGL;  				} -				if (fontp->loadFace(*font_paths_it, point_size_scale, -								 LLFontGL::sVertDPI, LLFontGL::sHorizDPI, 2, is_fallback, i)) +				if (fontp->loadFace(font_path, point_size_scale, +								 LLFontGL::sVertDPI, LLFontGL::sHorizDPI, is_fallback, i))  				{  					is_font_loaded = true;  					if (is_first_found) @@ -508,7 +528,8 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc)  					}  					else  					{ -						fontlist.push_back(fontp->mFontFreetype); +						result->mFontFreetype->addFallbackFont(fontp->mFontFreetype, font_file_it->CharFunctor); +  						delete fontp;  						fontp = NULL;  					} @@ -523,17 +544,12 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc)  		}  		if(!is_font_loaded)  		{ -			LL_INFOS_ONCE("LLFontRegistry") << "Couldn't load font " << *file_name_it <<  LL_ENDL; +			LL_INFOS_ONCE("LLFontRegistry") << "Couldn't load font " << font_file_it->FileName <<  LL_ENDL;  			delete fontp;  			fontp = NULL;  		}  	} -	if (result && !fontlist.empty()) -	{ -		result->mFontFreetype->setFallbackFonts(fontlist); -	} -  	if (result)  	{  		result->mFontDescriptor = desc; @@ -720,11 +736,22 @@ void LLFontRegistry::dump()  				<< " size=[" << desc.getSize() << "]"  				<< " fileNames="  				<< LL_ENDL; -		for (string_vec_t::const_iterator file_it=desc.getFileNames().begin(); -			 file_it != desc.getFileNames().end(); +		for (font_file_info_vec_t::const_iterator file_it=desc.getFontFiles().begin(); +			 file_it != desc.getFontFiles().end();  			 ++file_it)  		{ -			LL_INFOS() << "  file: " << *file_it <<LL_ENDL; +			LL_INFOS() << "  file: " << file_it->FileName << LL_ENDL; +		} +	} +} + +void LLFontRegistry::dumpTextures() +{ +	for (const auto& fontEntry : mFontMap) +	{ +		if (fontEntry.second) +		{ +			fontEntry.second->dumpTextures();  		}  	}  } diff --git a/indra/llrender/llfontregistry.h b/indra/llrender/llfontregistry.h index e30c81c630..b0ef72c5de 100644 --- a/indra/llrender/llfontregistry.h +++ b/indra/llrender/llfontregistry.h @@ -34,13 +34,32 @@ class LLFontGL;  typedef std::vector<std::string> string_vec_t; +struct LLFontFileInfo +{ +	LLFontFileInfo(const std::string& file_name, const std::function<bool(llwchar)>& char_functor = nullptr) +		: FileName(file_name) +		, CharFunctor(char_functor) +	{ +	} + +	LLFontFileInfo(const LLFontFileInfo& ffi) +		: FileName(ffi.FileName) +		, CharFunctor(ffi.CharFunctor) +	{ +	} + +	std::string FileName; +	std::function<bool(llwchar)> CharFunctor; +}; +typedef std::vector<LLFontFileInfo> font_file_info_vec_t; +  class LLFontDescriptor  {  public:  	LLFontDescriptor();  	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style); -	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style, const string_vec_t& file_names); -	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style, const string_vec_t& file_names, const string_vec_t& font_collections); +	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style, const font_file_info_vec_t& font_list); +	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style, const font_file_info_vec_t& font_list, const font_file_info_vec_t& font_collection_list);  	LLFontDescriptor normalize() const;  	bool operator<(const LLFontDescriptor& b) const; @@ -51,19 +70,26 @@ public:  	void setName(const std::string& name) { mName = name; }  	const std::string& getSize() const { return mSize; }  	void setSize(const std::string& size) { mSize = size; } -	const std::vector<std::string>& getFileNames() const { return mFileNames; } -	std::vector<std::string>& getFileNames() { return mFileNames; } -	const std::vector<std::string>& getFontCollectionsList() const { return mFontCollectionsList; } -	std::vector<std::string>& getFontCollectionsList() { return mFontCollectionsList; } + +	void addFontFile(const std::string& file_name, const std::string& char_functor = LLStringUtil::null); +	const font_file_info_vec_t & getFontFiles() const { return mFontFiles; } +	void setFontFiles(const font_file_info_vec_t& font_files) { mFontFiles = font_files; } +	void addFontCollectionFile(const std::string& file_name, const std::string& char_functor = LLStringUtil::null); +	const font_file_info_vec_t& getFontCollectionFiles() const { return mFontCollectionFiles; } +	void setFontCollectionFiles(const font_file_info_vec_t& font_collection_files) { mFontCollectionFiles = font_collection_files; } +  	const U8 getStyle() const { return mStyle; }  	void setStyle(U8 style) { mStyle = style; }  private:  	std::string mName;  	std::string mSize; -	string_vec_t mFileNames; -	string_vec_t mFontCollectionsList; +	font_file_info_vec_t mFontFiles; +	font_file_info_vec_t mFontCollectionFiles;  	U8 mStyle; + +	typedef std::map<std::string, std::function<bool(llwchar)>> char_functor_map_t; +	static char_functor_map_t mCharFunctors;  };  class LLFontRegistry @@ -94,6 +120,7 @@ public:  	bool nameToSize(const std::string& size_name, F32& size);  	void dump(); +	void dumpTextures();  	const string_vec_t& getUltimateFallbackList() const; diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index e0579352d3..44c61dcdbc 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -53,6 +53,8 @@ set(llui_SOURCE_FILES      lldockcontrol.cpp      lldraghandle.cpp      lleditmenuhandler.cpp +	llemojidictionary.cpp +	llemojihelper.cpp      llf32uictrl.cpp      llfiltereditor.cpp      llflashtimer.cpp @@ -163,6 +165,8 @@ set(llui_HEADER_FILES      lldockablefloater.h      lldockcontrol.h      lleditmenuhandler.h +	llemojidictionary.h +	llemojihelper.h      llf32uictrl.h      llfiltereditor.h       llflashtimer.h diff --git a/indra/llui/llemojidictionary.cpp b/indra/llui/llemojidictionary.cpp new file mode 100644 index 0000000000..b70a9b2e7a --- /dev/null +++ b/indra/llui/llemojidictionary.cpp @@ -0,0 +1,200 @@ +/** +* @file llemojidictionary.cpp +* @brief Implementation of LLEmojiDictionary +* +* $LicenseInfo:firstyear=2014&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2014, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ + +#include "linden_common.h" + +#include "lldir.h" +#include "llemojidictionary.h" +#include "llsdserialize.h" + +#include <boost/algorithm/string.hpp> +#include <boost/range/adaptor/filtered.hpp> +#include <boost/range/algorithm/transform.hpp> + +// ============================================================================ +// Constants +// + +constexpr char SKINNED_EMOJI_FILENAME[] = "emoji_characters.xml"; + +// ============================================================================ +// Helper functions +// + +template<class T> +std::list<T> llsd_array_to_list(const LLSD& sd, std::function<void(T&)> mutator = {}); + +template<> +std::list<std::string> llsd_array_to_list(const LLSD& sd, std::function<void(std::string&)> mutator) +{ +	std::list<std::string> result; +	for (LLSD::array_const_iterator it = sd.beginArray(), end = sd.endArray(); it != end; ++it) +	{ +		const LLSD& entry = *it; +		if (!entry.isString()) +			continue; + +		result.push_back(entry.asStringRef()); +		if (mutator) +		{ +			mutator(result.back()); +		} +	} +	return result; +} + +LLEmojiDescriptor::LLEmojiDescriptor(const LLSD& descriptor_sd) +{ +	Name = descriptor_sd["Name"].asStringRef(); + +	const LLWString emoji_string = utf8str_to_wstring(descriptor_sd["Character"].asString()); +	Character = (1 == emoji_string.size()) ? emoji_string[0] : L'\0'; // We don't currently support character composition + +	auto toLower = [](std::string& str) { LLStringUtil::toLower(str); }; +	ShortCodes = llsd_array_to_list<std::string>(descriptor_sd["ShortCodes"], toLower); +	Categories = llsd_array_to_list<std::string>(descriptor_sd["Categories"], toLower); + +	if (Name.empty()) +	{ +		Name = ShortCodes.front(); +	} +} + +bool LLEmojiDescriptor::isValid() const +{ +	return +		Character && +		!ShortCodes.empty() && +		!Categories.empty(); +} + +struct emoji_filter_base +{ +	emoji_filter_base(const std::string& needle) +	{ +		// Search without the colon (if present) so the user can type ':food' and see all emojis in the 'Food' category +		mNeedle = (boost::starts_with(needle, ":")) ? needle.substr(1) : needle; +		LLStringUtil::toLower(mNeedle); +	} + +protected: +	std::string mNeedle; +}; + +struct emoji_filter_shortcode_or_category_contains : public emoji_filter_base +{ +	emoji_filter_shortcode_or_category_contains(const std::string& needle) : emoji_filter_base(needle) {} + +	bool operator()(const LLEmojiDescriptor& descr) const +	{ +		for (const auto& short_code : descr.ShortCodes) +		{ +			if (boost::icontains(short_code, mNeedle)) +				return true; +		} + +		for (const auto& category : descr.Categories) +		{ +			if (boost::icontains(category, mNeedle)) +				return true; +		} + +		return false; +	} +}; + +// ============================================================================ +// LLEmojiDictionary class +// + +LLEmojiDictionary::LLEmojiDictionary() +{ +} + +// static +void LLEmojiDictionary::initClass() +{ +	LLEmojiDictionary* pThis = &LLEmojiDictionary::initParamSingleton(); + +	LLSD data; + +	const std::string filename = gDirUtilp->findSkinnedFilenames(LLDir::XUI, SKINNED_EMOJI_FILENAME, LLDir::CURRENT_SKIN).front(); +	llifstream file(filename.c_str()); +	if (file.is_open()) +	{ +		LL_DEBUGS() << "Loading emoji characters file at " << filename << LL_ENDL; +		LLSDSerialize::fromXML(data, file); +	} + +	if (data.isUndefined()) +	{ +		LL_WARNS() << "Emoji file characters missing or ill-formed" << LL_ENDL; +		return; +	} + +	for (LLSD::array_const_iterator descriptor_it = data.beginArray(), descriptor_end = data.endArray(); descriptor_it != descriptor_end; ++descriptor_it) +	{ +		LLEmojiDescriptor descriptor(*descriptor_it); +		if (!descriptor.isValid()) +		{ +			LL_WARNS() << "Skipping invalid emoji descriptor " << descriptor.Character << LL_ENDL; +			continue; +		} +		pThis->addEmoji(std::move(descriptor)); +	} +} + +LLWString LLEmojiDictionary::findMatchingEmojis(const std::string& needle) const +{ +	LLWString result; +	boost::transform(mEmojis | boost::adaptors::filtered(emoji_filter_shortcode_or_category_contains(needle)), +		             std::back_inserter(result), [](const auto& descr) { return descr.Character; }); +	return result; +} + +const LLEmojiDescriptor* LLEmojiDictionary::getDescriptorFromShortCode(const std::string& short_code) const +{ +	const auto it = mShortCode2Descr.find(short_code); +	return (mShortCode2Descr.end() != it) ? it->second : nullptr; +} + +std::string LLEmojiDictionary::getNameFromEmoji(llwchar ch) const +{ +	const auto it = mEmoji2Descr.find(ch); +	return (mEmoji2Descr.end() != it) ? it->second->Name : LLStringUtil::null; +} + +void LLEmojiDictionary::addEmoji(LLEmojiDescriptor&& descr) +{ +	mEmojis.push_back(descr); +	mEmoji2Descr.insert(std::make_pair(descr.Character, &mEmojis.back())); +	for (const std::string& shortCode : descr.ShortCodes) +	{ +		mShortCode2Descr.insert(std::make_pair(shortCode, &mEmojis.back())); +	} +} + +// ============================================================================ diff --git a/indra/llui/llemojidictionary.h b/indra/llui/llemojidictionary.h new file mode 100644 index 0000000000..46a61f1cd7 --- /dev/null +++ b/indra/llui/llemojidictionary.h @@ -0,0 +1,73 @@ +/** +* @file llemojidictionary.h +* @brief Header file for LLEmojiDictionary +* +* $LicenseInfo:firstyear=2014&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2014, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ + +#pragma once + +#include "lldictionary.h" +#include "llinitdestroyclass.h" +#include "llsingleton.h" + +// ============================================================================ +// LLEmojiDescriptor class +// + +struct LLEmojiDescriptor +{ +	LLEmojiDescriptor(const LLSD& descriptor_sd); + +	bool isValid() const; + +	std::string            Name; +	llwchar                Character; +	std::list<std::string> ShortCodes; +	std::list<std::string> Categories; +}; + +// ============================================================================ +// LLEmojiDictionary class +// + +class LLEmojiDictionary : public LLParamSingleton<LLEmojiDictionary>, public LLInitClass<LLEmojiDictionary> +{ +	LLSINGLETON(LLEmojiDictionary); +	~LLEmojiDictionary() override {}; + +public: +	static void initClass(); +	LLWString   findMatchingEmojis(const std::string& needle) const; +	const LLEmojiDescriptor* getDescriptorFromShortCode(const std::string& short_code) const; +	std::string getNameFromEmoji(llwchar ch) const; + +private: +	void addEmoji(LLEmojiDescriptor&& descr); + +private: +	std::list<LLEmojiDescriptor> mEmojis; +	std::map<llwchar, const LLEmojiDescriptor*> mEmoji2Descr; +	std::map<std::string, const LLEmojiDescriptor*> mShortCode2Descr; +}; + +// ============================================================================ diff --git a/indra/llui/llemojihelper.cpp b/indra/llui/llemojihelper.cpp new file mode 100644 index 0000000000..1e4c19a183 --- /dev/null +++ b/indra/llui/llemojihelper.cpp @@ -0,0 +1,166 @@ +/** +* @file llemojihelper.h +* @brief Header file for LLEmojiHelper +* +* $LicenseInfo:firstyear=2014&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2014, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ + +#include "linden_common.h" + +#include "llemojidictionary.h" +#include "llemojihelper.h" +#include "llfloater.h" +#include "llfloaterreg.h" +#include "lluictrl.h" + +// ============================================================================ +// Constants +// + +constexpr char DEFAULT_EMOJI_HELPER_FLOATER[] = "emoji_complete"; +constexpr S32 HELPER_FLOATER_OFFSET_X = 20; +constexpr S32 HELPER_FLOATER_OFFSET_Y = 0; + +// ============================================================================ +// LLEmojiHelper +// + +std::string LLEmojiHelper::getToolTip(llwchar ch) const +{ +	return LLEmojiDictionary::instance().getNameFromEmoji(ch); +} + +bool LLEmojiHelper::isActive(const LLUICtrl* ctrl_p) const +{ +	return mHostHandle.get() == ctrl_p; +} + +// static +bool LLEmojiHelper::isCursorInEmojiCode(const LLWString& wtext, S32 cursorPos, S32* pShortCodePos) +{ +	// If the cursor is currently on a colon start the check one character further back +	S32 shortCodePos = (cursorPos == 0 || L':' != wtext[cursorPos - 1]) ? cursorPos : cursorPos - 1; + +	auto isPartOfShortcode = [](llwchar ch) { +		switch (ch) +		{ +			case L'-': +			case L'_': +			case L'+': +				return true; +			default: +				return LLStringOps::isAlnum(ch); +		} +	}; +	while (shortCodePos > 1 && isPartOfShortcode(wtext[shortCodePos - 1])) +	{ +		shortCodePos--; +	} + +	bool isShortCode = (L':' == wtext[shortCodePos - 1]) && (cursorPos - shortCodePos >= 2); +	if (pShortCodePos) +		*pShortCodePos = (isShortCode) ? shortCodePos - 1 : -1; +	return isShortCode; +} + +void LLEmojiHelper::showHelper(LLUICtrl* hostctrl_p, S32 local_x, S32 local_y, const std::string& short_code, std::function<void(LLWString)> cb) +{ +	// Commit immediately if the user already typed a full shortcode +	if (const auto* emojiDescrp = LLEmojiDictionary::instance().getDescriptorFromShortCode(short_code)) +	{ +		cb(LLWString(1, emojiDescrp->Character)); +		hideHelper(); +		return; +	} + +	if (mHelperHandle.isDead()) +	{ +		LLFloater* pHelperFloater = LLFloaterReg::getInstance(DEFAULT_EMOJI_HELPER_FLOATER); +		mHelperHandle = pHelperFloater->getHandle(); +		mHelperCommitConn = pHelperFloater->setCommitCallback(std::bind([&](const LLSD& sdValue) { onCommitEmoji(utf8str_to_wstring(sdValue.asStringRef())); }, std::placeholders::_2)); +	} +	setHostCtrl(hostctrl_p); +	mEmojiCommitCb = cb; + +	S32 floater_x, floater_y; +	if (!hostctrl_p->localPointToOtherView(local_x, local_y, &floater_x, &floater_y, gFloaterView)) +	{ +		LL_ERRS() << "Cannot show emoji helper for non-floater controls." << LL_ENDL; +		return; +	} + +	LLFloater* pHelperFloater = mHelperHandle.get(); +	LLRect rct = pHelperFloater->getRect(); +	rct.setLeftTopAndSize(floater_x - HELPER_FLOATER_OFFSET_X, floater_y - HELPER_FLOATER_OFFSET_Y + rct.getHeight(), rct.getWidth(), rct.getHeight()); +	pHelperFloater->setRect(rct); +	pHelperFloater->openFloater(LLSD().with("hint", short_code)); +} + +void LLEmojiHelper::hideHelper(const LLUICtrl* ctrl_p) +{ +	if (ctrl_p && !isActive(ctrl_p)) +	{ +		return; +	} + +	setHostCtrl(nullptr); +} + +bool LLEmojiHelper::handleKey(const LLUICtrl* ctrl_p, KEY key, MASK mask) +{ +	if (mHelperHandle.isDead() || !isActive(ctrl_p)) +	{ +		return false; +	} + +	return mHelperHandle.get()->handleKey(key, mask, true); +} + +void LLEmojiHelper::onCommitEmoji(const LLWString& wstr) +{ +	if (!mHostHandle.isDead() && mEmojiCommitCb) +	{ +		mEmojiCommitCb(wstr); +	} +} + +void LLEmojiHelper::setHostCtrl(LLUICtrl* hostctrl_p) +{ +	const LLUICtrl* pCurHostCtrl = mHostHandle.get(); +	if (pCurHostCtrl != hostctrl_p) +	{ +		mHostCtrlFocusLostConn.disconnect(); +		mHostHandle.markDead(); +		mEmojiCommitCb = {}; + +		if (!mHelperHandle.isDead()) +		{ +			mHelperHandle.get()->closeFloater(); +		} + +		if (hostctrl_p) +		{ +			mHostHandle = hostctrl_p->getHandle(); +			mHostCtrlFocusLostConn = hostctrl_p->setFocusLostCallback(std::bind([&]() { hideHelper(getHostCtrl()); })); +		} +	} +} diff --git a/indra/llui/llemojihelper.h b/indra/llui/llemojihelper.h new file mode 100644 index 0000000000..63f5c640c9 --- /dev/null +++ b/indra/llui/llemojihelper.h @@ -0,0 +1,64 @@ +/** +* @file llemojihelper.h +* @brief Header file for LLEmojiHelper +* +* $LicenseInfo:firstyear=2014&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2014, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ + +#pragma once + +#include "llhandle.h" +#include "llsingleton.h" + +#include <boost/signals2.hpp> + +class LLFloater; +class LLUICtrl; + +class LLEmojiHelper : public LLSingleton<LLEmojiHelper> +{ +	LLSINGLETON(LLEmojiHelper) {} +	~LLEmojiHelper() override {} + +public: +	// General +	std::string getToolTip(llwchar ch) const; +	bool        isActive(const LLUICtrl* ctrl_p) const; +	static bool isCursorInEmojiCode(const LLWString& wtext, S32 cursor_pos, S32* short_code_pos_p = nullptr); +	void        showHelper(LLUICtrl* hostctrl_p, S32 local_x, S32 local_y, const std::string& short_code, std::function<void(LLWString)> commit_cb); +	void        hideHelper(const LLUICtrl* ctrl_p = nullptr); + +	// Eventing +	bool handleKey(const LLUICtrl* ctrl_p, KEY key, MASK mask); +	void onCommitEmoji(const LLWString& wstr); + +protected: +	LLUICtrl* getHostCtrl() const { return mHostHandle.get(); } +	void      setHostCtrl(LLUICtrl* hostctrl_p); + +private: +	LLHandle<LLUICtrl>  mHostHandle; +	LLHandle<LLFloater> mHelperHandle; +	boost::signals2::connection mHostCtrlFocusLostConn; +	boost::signals2::connection mHelperCommitConn; +	std::function<void(LLWString)> mEmojiCommitCb; +}; diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index d413fab270..763b67bb3a 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -2486,7 +2486,7 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus, BOOL restore  	if (mFrontChild == child)  	{ -		if (give_focus && !gFocusMgr.childHasKeyboardFocus(child)) +		if (give_focus && child->canFocusStealFrontmost() && !gFocusMgr.childHasKeyboardFocus(child))  		{  			child->setFocus(TRUE);  		} @@ -3039,7 +3039,34 @@ void LLFloaterView::syncFloaterTabOrder()  			LLFloater* floaterp = dynamic_cast<LLFloater*>(*child_it);  			if (gFocusMgr.childHasKeyboardFocus(floaterp))  			{ -				bringToFront(floaterp, FALSE); +                if (mFrontChild != floaterp) +                { +                    // Grab a list of the top floaters that want to stay on top of the focused floater +					std::list<LLFloater*> listTop; +					if (mFrontChild && !mFrontChild->canFocusStealFrontmost()) +                    { +                        for (LLView* childp : *getChildList()) +                        { +							LLFloater* child_floaterp = static_cast<LLFloater*>(childp); +                            if (child_floaterp->canFocusStealFrontmost()) +                                break; +							listTop.push_back(child_floaterp); +                        } +                    } + +                    bringToFront(floaterp, FALSE); + +                    // Restore top floaters +					if (!listTop.empty()) +					{ +						for (LLView* childp : listTop) +						{ +							sendChildToFront(childp); +						} +						mFrontChild = listTop.back(); +					} +                } +  				break;  			}  		} diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 668cd208a9..282f7a80ac 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -313,6 +313,9 @@ public:  	/*virtual*/ void setVisible(BOOL visible); // do not override  	/*virtual*/ void onVisibilityChange ( BOOL new_visibility ); // do not override +	bool            canFocusStealFrontmost() const { return mFocusStealsFrontmost; } +	void            setFocusStealsFrontmost(bool wants_frontmost) { mFocusStealsFrontmost = wants_frontmost; } +  	void			setFrontmost(BOOL take_focus = TRUE, BOOL restore = TRUE);       virtual void	setVisibleAndFrontmost(BOOL take_focus=TRUE, const LLSD& key = LLSD()); @@ -481,6 +484,7 @@ private:  	BOOL			mCanTearOff;  	BOOL			mCanMinimize;  	BOOL			mCanClose; +    bool            mFocusStealsFrontmost = true;	// FALSE if we don't want the currently focused floater to cover this floater without user interaction  	BOOL			mDragOnLeft;  	BOOL			mResizable; diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index eba93beed9..d5988dadbc 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -874,7 +874,7 @@ void LLFolderViewItem::drawLabel(const LLFontGL * font, const F32 x, const F32 y      //      font->renderUTF8(mLabel, 0, x, y, color,          LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, -        S32_MAX, getRect().getWidth() - (S32) x - mLabelPaddingRight, &right_x, TRUE); +        S32_MAX, getRect().getWidth() - (S32) x - mLabelPaddingRight, &right_x, /*use_ellipses*/TRUE, /*use_color*/FALSE);  }  void LLFolderViewItem::draw() @@ -953,7 +953,7 @@ void LLFolderViewItem::draw()  	{  		font->renderUTF8( mLabelSuffix, 0, right_x, y, isFadeItem() ? color : (LLColor4)sSuffixColor,  						  LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, -						  S32_MAX, S32_MAX, &right_x, FALSE ); +						  S32_MAX, S32_MAX, &right_x, /*use_ellipses*/FALSE, /*use_color*/FALSE );  	}  	//--------------------------------------------------------------------------------// @@ -966,7 +966,7 @@ void LLFolderViewItem::draw()          F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;          font->renderUTF8( combined_string, filter_offset, match_string_left, yy,              sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, -            filter_string_length, S32_MAX, &right_x, FALSE ); +            filter_string_length, S32_MAX, &right_x, /*use_ellipses*/FALSE, /*use_color*/FALSE );      }      //Gilbert Linden 9-20-2012: Although this should be legal, removing it because it causes the mLabelSuffix rendering to diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 8effd866e0..b2c0577604 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -29,6 +29,7 @@  #include "lltextbase.h" +#include "llemojihelper.h"  #include "lllocalcliprect.h"  #include "llmenugl.h"  #include "llscrollcontainer.h" @@ -161,10 +162,12 @@ LLTextBase::Params::Params()  	line_spacing("line_spacing"),  	max_text_length("max_length", 255),  	font_shadow("font_shadow"), +	text_valign("text_valign"),  	wrap("wrap"),  	trusted_content("trusted_content", true),  	always_show_icons("always_show_icons", false),  	use_ellipses("use_ellipses", false), +	use_color("use_color", false),  	parse_urls("parse_urls", false),  	force_urls_external("force_urls_external", false),  	parse_highlights("parse_highlights", false) @@ -208,6 +211,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)  	mVPad(p.v_pad),  	mHAlign(p.font_halign),  	mVAlign(p.font_valign), +	mTextVAlign(p.text_valign.isProvided() ? p.text_valign.getValue() : p.font_valign.getValue()),  	mLineSpacingMult(p.line_spacing.multiple),  	mLineSpacingPixels(p.line_spacing.pixels),  	mClip(p.clip), @@ -222,6 +226,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)  	mPlainText ( p.plain_text ),  	mWordWrap(p.wrap),  	mUseEllipses( p.use_ellipses ), +	mUseColor(p.use_color),  	mParseHTML(p.parse_urls),  	mForceUrlsExternal(p.force_urls_external),  	mParseHighlights(p.parse_highlights), @@ -576,7 +581,7 @@ void LLTextBase::drawCursor()  				fontp = segmentp->getStyle()->getFont();  				fontp->render(text, mCursorPos, cursor_rect,   					LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], alpha), -					LLFontGL::LEFT, mVAlign, +					LLFontGL::LEFT, mTextVAlign,  					LLFontGL::NORMAL,  					LLFontGL::NO_SHADOW,  					1); @@ -890,6 +895,25 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s  		}  	} +	// Insert special segments where necessary (insertSegment takes care of splitting normal text segments around them for us) +	{ +		LLStyleSP emoji_style; +		for (S32 text_kitty = 0, text_len = wstr.size(); text_kitty < text_len; text_kitty++) +		{ +			if (LLStringOps::isEmoji(wstr[text_kitty])) +			{ +				if (!emoji_style) +				{ +					emoji_style = new LLStyle(getStyleParams()); +					emoji_style->setFont(LLFontGL::getFontEmoji()); +				} + +				S32 new_seg_start = pos + text_kitty; +				insertSegment(new LLEmojiTextSegment(emoji_style, new_seg_start, new_seg_start + 1, *this)); +			} +		} +	} +  	getViewModel()->getEditableDisplay().insert(pos, wstr);  	if ( truncate() ) @@ -1986,8 +2010,6 @@ LLTextBase::segment_set_t::iterator LLTextBase::getSegIterContaining(S32 index)  		text_len = mLabel.getWString().length();  	} -	if (index > text_len) { return mSegments.end(); } -  	// when there are no segments, we return the end iterator, which must be checked by caller  	if (mSegments.size() <= 1) { return mSegments.begin(); } @@ -2011,8 +2033,6 @@ LLTextBase::segment_set_t::const_iterator LLTextBase::getSegIterContaining(S32 i  		text_len = mLabel.getWString().length();  	} -	if (index > text_len) { return mSegments.end(); } -  	// when there are no segments, we return the end iterator, which must be checked by caller  	if (mSegments.size() <= 1) { return mSegments.begin(); } @@ -3306,12 +3326,13 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele  		font->render(text, start,   				 rect,   				 color,  -				 LLFontGL::LEFT, mEditor.mVAlign,  +				 LLFontGL::LEFT, mEditor.mTextVAlign,  				 LLFontGL::NORMAL,   				 mStyle->getShadowType(),   				 length,  				 &right_x,  -				 mEditor.getUseEllipses()); +				 mEditor.getUseEllipses(), +				 mEditor.getUseColor());  	}  	rect.mLeft = right_x; @@ -3325,12 +3346,13 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele  		font->render(text, start,   				 rect,  				 mStyle->getSelectedColor().get(), -				 LLFontGL::LEFT, mEditor.mVAlign,  +				 LLFontGL::LEFT, mEditor.mTextVAlign,  				 LLFontGL::NORMAL,   				 LLFontGL::NO_SHADOW,   				 length,  				 &right_x,  -				 mEditor.getUseEllipses()); +				 mEditor.getUseEllipses(), +				 mEditor.getUseColor());  	}  	rect.mLeft = right_x;  	if( selection_end < seg_end ) @@ -3342,12 +3364,13 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele  		font->render(text, start,   				 rect,   				 color,  -				 LLFontGL::LEFT, mEditor.mVAlign,  +				 LLFontGL::LEFT, mEditor.mTextVAlign,  				 LLFontGL::NORMAL,   				 mStyle->getShadowType(),   				 length,  				 &right_x,  -				 mEditor.getUseEllipses()); +				 mEditor.getUseEllipses(), +				 mEditor.getUseColor());  	}      return right_x;  } @@ -3579,6 +3602,33 @@ const S32 LLLabelTextSegment::getLength() const  }  // +// LLEmojiTextSegment +// +LLEmojiTextSegment::LLEmojiTextSegment(LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor) +	: LLNormalTextSegment(style, start, end, editor) +{ +} + +LLEmojiTextSegment::LLEmojiTextSegment(const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible) +	: LLNormalTextSegment(color, start, end, editor, is_visible) +{ +} + +BOOL LLEmojiTextSegment::handleToolTip(S32 x, S32 y, MASK mask) +{ +	if (mTooltip.empty()) +	{ +		LLWString emoji = getWText().substr(getStart(), getEnd() - getStart()); +		if (!emoji.empty()) +		{ +			mTooltip = LLEmojiHelper::instance().getToolTip(emoji[0]); +		} +	} + +	return LLNormalTextSegment::handleToolTip(x, y, mask); +} + +//  // LLOnHoverChangeableTextSegment  // diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index e3cf56a5ee..7e1f727607 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -178,6 +178,18 @@ protected:  	/*virtual*/	const S32			getLength()	const;  }; +// Text segment that represents a single emoji character that has a different style (=font size) than the rest of +// the document it belongs to +class LLEmojiTextSegment : public LLNormalTextSegment +{ +public: +	LLEmojiTextSegment(LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor); +	LLEmojiTextSegment(const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible = TRUE); + +	bool canEdit() const override { return false; } +	BOOL handleToolTip(S32 x, S32 y, MASK mask); +}; +  // Text segment that changes it's style depending of mouse pointer position ( is it inside or outside segment)  class LLOnHoverChangeableTextSegment : public LLNormalTextSegment  { @@ -316,6 +328,7 @@ public:  								plain_text,  								wrap,  								use_ellipses, +								use_color,  								parse_urls,  								force_urls_external,  								parse_highlights, @@ -335,6 +348,8 @@ public:  		Optional<LLFontGL::ShadowType>	font_shadow; +		Optional<LLFontGL::VAlign> text_valign; +  		Params();  	}; @@ -394,6 +409,7 @@ public:  	// used by LLTextSegment layout code  	bool					getWordWrap() { return mWordWrap; }  	bool					getUseEllipses() { return mUseEllipses; } +	bool					getUseColor() { return mUseColor; }  	bool					truncate(); // returns true of truncation occurred  	bool					isContentTrusted() {return mTrustedContent;} @@ -687,8 +703,9 @@ protected:  	// configuration  	S32							mHPad;				// padding on left of text  	S32							mVPad;				// padding above text -	LLFontGL::HAlign			mHAlign; -	LLFontGL::VAlign			mVAlign; +	LLFontGL::HAlign			mHAlign;			// horizontal alignment of the document in its entirety +	LLFontGL::VAlign			mVAlign;			// vertical alignment of the document in its entirety +	LLFontGL::VAlign			mTextVAlign;		// vertical alignment of a text segment within a single line of text  	F32							mLineSpacingMult;	// multiple of line height used as space for a single line of text (e.g. 1.5 to get 50% padding)  	S32							mLineSpacingPixels;	// padding between lines  	bool						mBorderVisible; @@ -697,6 +714,7 @@ protected:  	bool						mParseHighlights;	// highlight user-defined keywords  	bool                		mWordWrap;  	bool						mUseEllipses; +	bool						mUseColor;  	bool						mTrackEnd;			// if true, keeps scroll position at end of document during resize  	bool						mReadOnly;  	bool						mBGVisible;			// render background? diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index b1f8b00cab..168c260c7d 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -43,6 +43,7 @@  #include "llmath.h"  #include "llclipboard.h" +#include "llemojihelper.h"  #include "llscrollbar.h"  #include "llstl.h"  #include "llstring.h" @@ -238,6 +239,7 @@ LLTextEditor::Params::Params()  	default_color("default_color"),      commit_on_focus_lost("commit_on_focus_lost", false),  	show_context_menu("show_context_menu"), +	show_emoji_helper("show_emoji_helper"),  	enable_tooltip_paste("enable_tooltip_paste")  {  	addSynonym(prevalidate_callback, "text_type"); @@ -259,6 +261,7 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :  	mPrevalidateFunc(p.prevalidate_callback()),  	mContextMenu(NULL),  	mShowContextMenu(p.show_context_menu), +	mShowEmojiHelper(p.show_emoji_helper),  	mEnableTooltipPaste(p.enable_tooltip_paste),  	mPassDelete(FALSE),  	mKeepSelectionOnReturn(false) @@ -501,6 +504,15 @@ void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out,  	}  } +void LLTextEditor::setShowEmojiHelper(bool show) { +	if (!mShowEmojiHelper) +	{ +		LLEmojiHelper::instance().hideHelper(this); +	} + +	mShowEmojiHelper = show; +} +  BOOL LLTextEditor::selectionContainsLineBreaks()  {  	if (hasSelection()) @@ -664,6 +676,21 @@ void LLTextEditor::selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_p  	endSelection();  } +void LLTextEditor::handleEmojiCommit(const LLWString& wstr) +{ +	LLWString wtext(getWText()); S32 shortCodePos; +	if (LLEmojiHelper::isCursorInEmojiCode(wtext, mCursorPos, &shortCodePos)) +	{ +		remove(shortCodePos, mCursorPos - shortCodePos, true); + +		auto styleParams = LLStyle::Params(); +		styleParams.font = LLFontGL::getFontEmoji(); +		insert(shortCodePos, wstr, false, new LLEmojiTextSegment(new LLStyle(styleParams), shortCodePos, shortCodePos + wstr.size(), *this)); + +		setCursorPos(shortCodePos + 1); +	} +} +  BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)  {  	BOOL	handled = FALSE; @@ -930,6 +957,12 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)  S32 LLTextEditor::execute( TextCmd* cmd )  { +	if (!mReadOnly && mShowEmojiHelper) +	{ +		// Any change to our contents should always hide the helper +		LLEmojiHelper::instance().hideHelper(this); +	} +  	S32 delta = 0;  	if( cmd->execute(this, &delta) )  	{ @@ -1124,6 +1157,17 @@ void LLTextEditor::addChar(llwchar wc)  	setCursorPos(mCursorPos + addChar( mCursorPos, wc )); +	if (!mReadOnly && mShowEmojiHelper) +	{ +		LLWString wtext(getWText()); S32 shortCodePos; +		if (LLEmojiHelper::isCursorInEmojiCode(wtext, mCursorPos, &shortCodePos)) +		{ +			const LLRect cursorRect = getLocalRectFromDocIndex(mCursorPos - 1); +			const LLWString shortCode = wtext.substr(shortCodePos, mCursorPos - shortCodePos); +			LLEmojiHelper::instance().showHelper(this, cursorRect.mLeft, cursorRect.mTop, wstring_to_utf8str(shortCode), std::bind(&LLTextEditor::handleEmojiCommit, this, std::placeholders::_1)); +		} +	} +  	if (!mReadOnly && mAutoreplaceCallback != NULL)  	{  		// autoreplace the text, if necessary @@ -1774,6 +1818,11 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask )  	}  	else   	{ +		if (!mReadOnly && mShowEmojiHelper && LLEmojiHelper::instance().handleKey(this, key, mask)) +		{ +			return TRUE; +		} +  		if (mEnableTooltipPaste &&  			LLToolTipMgr::instance().toolTipVisible() &&   			KEY_TAB == key) @@ -1815,6 +1864,12 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask )  	{  		resetCursorBlink();  		needsScroll(); + +		if (mShowEmojiHelper) +		{ +			// Dismiss the helper whenever we handled a key that it didn't +			LLEmojiHelper::instance().hideHelper(this); +		}  	}  	return handled; diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 1a10d2fd1e..f7621b39f0 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -60,6 +60,7 @@ public:  								ignore_tab,  								commit_on_focus_lost,  								show_context_menu, +								show_emoji_helper,  								enable_tooltip_paste,  								auto_indent; @@ -91,6 +92,8 @@ public:  	static S32		spacesPerTab(); +	        void    handleEmojiCommit(const LLWString& wstr); +  	// mousehandler overrides  	virtual BOOL	handleMouseDown(S32 x, S32 y, MASK mask);  	virtual BOOL	handleMouseUp(S32 x, S32 y, MASK mask); @@ -202,6 +205,9 @@ public:  	void			setShowContextMenu(bool show) { mShowContextMenu = show; }  	bool			getShowContextMenu() const { return mShowContextMenu; } +	void			setShowEmojiHelper(bool show); +	bool			getShowEmojiHelper() const { return mShowEmojiHelper; } +  	void			setPassDelete(BOOL b) { mPassDelete = b; }  protected: @@ -318,6 +324,7 @@ private:  	BOOL			mAllowEmbeddedItems;  	bool			mShowContextMenu; +	bool			mShowEmojiHelper;  	bool			mEnableTooltipPaste;  	bool			mPassDelete;  	bool			mKeepSelectionOnReturn;	// disabling of removing selected text after pressing of Enter diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 9ba71913d0..74abe54690 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -1324,7 +1324,7 @@ void LLView::drawDebugRect()  										debug_rect.getWidth(), debug_rect.getHeight());  			LLFontGL::getFontSansSerifSmall()->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color,  												LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, -												S32_MAX, S32_MAX, NULL, FALSE); +												S32_MAX, S32_MAX, NULL, /*use_ellipses*/FALSE, /*use_color*/FALSE);  		}  	}  	LLUI::popMatrix(); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 9eee5338ec..36f4680c44 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -18,6 +18,7 @@ include(DragDrop)  include(EXPAT)  include(FMODSTUDIO)  include(Hunspell) +include(ICU4C)  include(JPEGEncoderBasic)  include(JsonCpp)  include(LLAppearance) @@ -448,6 +449,7 @@ set(viewer_SOURCE_FILES      llpaneleditsky.cpp      llpaneleditwater.cpp      llpaneleditwearable.cpp +	llpanelemojicomplete.cpp      llpanelenvironment.cpp      llpanelexperiencelisteditor.cpp      llpanelexperiencelog.cpp @@ -1079,6 +1081,7 @@ set(viewer_HEADER_FILES      llpaneleditsky.h      llpaneleditwater.h      llpaneleditwearable.h +	llpanelemojicomplete.h      llpanelenvironment.h      llpanelexperiencelisteditor.h      llpanelexperiencelog.h @@ -1503,6 +1506,12 @@ if (WINDOWS)          set(viewer_SOURCE_FILES "${viewer_SOURCE_FILES}" llviewerprecompiledheaders.cpp)      endif(USE_PRECOMPILED_HEADERS) +    message("Copying fonts") +    file(GLOB FONT_FILE_GLOB_LIST +      "${AUTOBUILD_INSTALL_DIR}/fonts/*" +    ) +    file(COPY ${FONT_FILE_GLOB_LIST} DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/fonts") +      # Replace the icons with the appropriate ones for the channel      # ('test' is the default)      set(ICON_PATH "test") @@ -2043,6 +2052,7 @@ target_link_libraries(${VIEWER_BINARY_NAME}      ${NDOF_LIBRARY}      ${NVAPI_LIBRARY}      ${HUNSPELL_LIBRARY} +    ${ICU4C_LIBRARY}      ${viewer_LIBRARIES}      ${BOOST_PROGRAM_OPTIONS_LIBRARY}      ${BOOST_REGEX_LIBRARY} diff --git a/indra/newview/fonts/DejaVu-license.txt b/indra/newview/fonts/DejaVu-license.txt deleted file mode 100644 index 254e2cc42a..0000000000 --- a/indra/newview/fonts/DejaVu-license.txt +++ /dev/null @@ -1,99 +0,0 @@ -Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. -Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) - -Bitstream Vera Fonts Copyright ------------------------------- - -Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is -a trademark of Bitstream, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of the fonts accompanying this license ("Fonts") and associated -documentation files (the "Font Software"), to reproduce and distribute the -Font Software, including without limitation the rights to use, copy, merge, -publish, distribute, and/or sell copies of the Font Software, and to permit -persons to whom the Font Software is furnished to do so, subject to the -following conditions: - -The above copyright and trademark notices and this permission notice shall -be included in all copies of one or more of the Font Software typefaces. - -The Font Software may be modified, altered, or added to, and in particular -the designs of glyphs or characters in the Fonts may be modified and -additional glyphs or characters may be added to the Fonts, only if the fonts -are renamed to names not containing either the words "Bitstream" or the word -"Vera". - -This License becomes null and void to the extent applicable to Fonts or Font -Software that has been modified and is distributed under the "Bitstream -Vera" names. - -The Font Software may be sold as part of a larger software package but no -copy of one or more of the Font Software typefaces may be sold by itself. - -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, -TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME -FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING -ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF -THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE -FONT SOFTWARE. - -Except as contained in this notice, the names of Gnome, the Gnome -Foundation, and Bitstream Inc., shall not be used in advertising or -otherwise to promote the sale, use or other dealings in this Font Software -without prior written authorization from the Gnome Foundation or Bitstream -Inc., respectively. For further information, contact: fonts at gnome dot -org.  - -Arev Fonts Copyright ------------------------------- - -Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of the fonts accompanying this license ("Fonts") and -associated documentation files (the "Font Software"), to reproduce -and distribute the modifications to the Bitstream Vera Font Software, -including without limitation the rights to use, copy, merge, publish, -distribute, and/or sell copies of the Font Software, and to permit -persons to whom the Font Software is furnished to do so, subject to -the following conditions: - -The above copyright and trademark notices and this permission notice -shall be included in all copies of one or more of the Font Software -typefaces. - -The Font Software may be modified, altered, or added to, and in -particular the designs of glyphs or characters in the Fonts may be -modified and additional glyphs or characters may be added to the -Fonts, only if the fonts are renamed to names not containing either -the words "Tavmjong Bah" or the word "Arev". - -This License becomes null and void to the extent applicable to Fonts -or Font Software that has been modified and is distributed under the  -"Tavmjong Bah Arev" names. - -The Font Software may be sold as part of a larger software package but -no copy of one or more of the Font Software typefaces may be sold by -itself. - -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL -TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. - -Except as contained in this notice, the name of Tavmjong Bah shall not -be used in advertising or otherwise to promote the sale, use or other -dealings in this Font Software without prior written authorization -from Tavmjong Bah. For further information, contact: tavmjong @ free -. fr. - -$Id: LICENSE 2133 2007-11-28 02:46:28Z lechimp $ diff --git a/indra/newview/fonts/DejaVuSans-Bold.ttf b/indra/newview/fonts/DejaVuSans-Bold.ttf Binary files differdeleted file mode 100644 index ec1a2ebaf2..0000000000 --- a/indra/newview/fonts/DejaVuSans-Bold.ttf +++ /dev/null diff --git a/indra/newview/fonts/DejaVuSans-BoldOblique.ttf b/indra/newview/fonts/DejaVuSans-BoldOblique.ttf Binary files differdeleted file mode 100644 index 1a5576460d..0000000000 --- a/indra/newview/fonts/DejaVuSans-BoldOblique.ttf +++ /dev/null diff --git a/indra/newview/fonts/DejaVuSans-Oblique.ttf b/indra/newview/fonts/DejaVuSans-Oblique.ttf Binary files differdeleted file mode 100644 index becc549927..0000000000 --- a/indra/newview/fonts/DejaVuSans-Oblique.ttf +++ /dev/null diff --git a/indra/newview/fonts/DejaVuSans.ttf b/indra/newview/fonts/DejaVuSans.ttf Binary files differdeleted file mode 100644 index c1b19d8705..0000000000 --- a/indra/newview/fonts/DejaVuSans.ttf +++ /dev/null diff --git a/indra/newview/fonts/DejaVuSansMono.ttf b/indra/newview/fonts/DejaVuSansMono.ttf Binary files differdeleted file mode 100644 index 6bc854ddae..0000000000 --- a/indra/newview/fonts/DejaVuSansMono.ttf +++ /dev/null diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 7188b9136c..9e4d8c5fa7 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -1074,6 +1074,8 @@ LLChatHistory::LLChatHistory(const LLChatHistory::Params& p)  	editor_params.enabled = false; // read only  	editor_params.show_context_menu = "true";  	editor_params.trusted_content = false; +	editor_params.text_valign = LLFontGL::VAlign::VCENTER; +	editor_params.use_color = true;  	mEditor = LLUICtrlFactory::create<LLTextEditor>(editor_params, this);  	mEditor->setIsFriendCallback(LLAvatarActions::isFriend);  	mEditor->setIsObjectBlockedCallback(boost::bind(&LLMuteList::isMuted, LLMuteList::getInstance(), _1, _2, 0)); diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp index c4c88d304c..e6b6b10408 100644 --- a/indra/newview/lldrawpoolbump.cpp +++ b/indra/newview/lldrawpoolbump.cpp @@ -78,7 +78,9 @@ static S32 cube_channel = -1;  static S32 diffuse_channel = -1;  static S32 bump_channel = -1; -#define LL_BUMPLIST_MULTITHREADED 0 // TODO -- figure out why this doesn't work +// Enabled after changing LLViewerTexture::mNeedsCreateTexture to an +// LLAtomicBool; this should work just fine, now. HB +#define LL_BUMPLIST_MULTITHREADED 1  // static  diff --git a/indra/newview/llexpandabletextbox.cpp b/indra/newview/llexpandabletextbox.cpp index 3395777aab..2d332f75f5 100644 --- a/indra/newview/llexpandabletextbox.cpp +++ b/indra/newview/llexpandabletextbox.cpp @@ -88,7 +88,7 @@ public:  									mStyle->getShadowType(),   									end - start, draw_rect.getWidth(),   									&right_x,  -									mEditor.getUseEllipses()); +									mEditor.getUseEllipses(), mEditor.getUseColor());  		return right_x;  	}  	/*virtual*/ bool	canEdit() const { return false; } diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp index e67c79a3a0..9d16faf0b5 100644 --- a/indra/newview/llfloateruipreview.cpp +++ b/indra/newview/llfloateruipreview.cpp @@ -1601,7 +1601,7 @@ void LLOverlapPanel::draw()  		LLUI::translate(5,getRect().getHeight()-20);	// translate to top-5,left-5  		LLView::sDrawPreviewHighlights = FALSE;  		LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection_text, 0, 0, 0, text_color, -				LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE); +				LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, /*use_ellipses*/FALSE, /*use_color*/FALSE);  	}  	else  	{ @@ -1619,7 +1619,7 @@ void LLOverlapPanel::draw()  			std::string current_selection = std::string(current_selection_text + LLView::sPreviewClickedElement->getName() + " (no elements overlap)");  			S32 text_width = LLFontGL::getFontSansSerifSmall()->getWidth(current_selection) + 10;  			LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection, 0, 0, 0, text_color, -					LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE); +					LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, /*use_ellipses*/FALSE, /*use_color*/FALSE);  			// widen panel enough to fit this text  			LLRect rect = getRect();  			setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() < text_width ? rect.mLeft + text_width : rect.mRight,rect.mTop)); @@ -1685,7 +1685,7 @@ void LLOverlapPanel::draw()  		// draw currently-selected element at top of overlappers  		LLUI::translate(0,-mSpacing);  		LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection_text + LLView::sPreviewClickedElement->getName(), 0, 0, 0, text_color, -				LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE); +				LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, /*use_ellipses*/FALSE, /*use_color*/FALSE);  		LLUI::translate(0,-mSpacing-LLView::sPreviewClickedElement->getRect().getHeight());	// skip spacing distance + height  		LLView::sPreviewClickedElement->draw(); @@ -1700,7 +1700,7 @@ void LLOverlapPanel::draw()  			// draw name  			LLUI::translate(0,-mSpacing);  			LLFontGL::getFontSansSerifSmall()->renderUTF8(overlapper_text + viewp->getName(), 0, 0, 0, text_color, -					LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE); +					LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, /*use_ellipses*/FALSE, /*use_color*/FALSE);  			// draw element  			LLUI::translate(0,-mSpacing-viewp->getRect().getHeight());	// skip spacing distance + height diff --git a/indra/newview/llhudrender.cpp b/indra/newview/llhudrender.cpp index dff310ecf9..c1f17c9d33 100644 --- a/indra/newview/llhudrender.cpp +++ b/indra/newview/llhudrender.cpp @@ -138,7 +138,7 @@ void hud_render_text(const LLWString &wstr, const LLVector3 &pos_agent,  	LLUI::translate((F32) winX*1.0f/LLFontGL::sScaleX, (F32) winY*1.0f/(LLFontGL::sScaleY), -(((F32) winZ*2.f)-1.f));  	F32 right_x; -	font.render(wstr, 0, 0, 1, color, LLFontGL::LEFT, LLFontGL::BASELINE, style, shadow, wstr.length(), 1000, &right_x); +	font.render(wstr, 0, 0, 1, color, LLFontGL::LEFT, LLFontGL::BASELINE, style, shadow, wstr.length(), 1000, &right_x, /*use_ellipses*/false, /*use_color*/true);  	LLUI::popMatrix();  	gGL.popMatrix(); diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp new file mode 100644 index 0000000000..8b89e3aa14 --- /dev/null +++ b/indra/newview/llpanelemojicomplete.cpp @@ -0,0 +1,321 @@ +/** +* @file llpanelemojicomplete.h +* @brief Header file for LLPanelEmojiComplete +* +* $LicenseInfo:firstyear=2012&license=lgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2011, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ + +#include "llviewerprecompiledheaders.h" + +#include "llemojidictionary.h" +#include "llemojihelper.h" +#include "llpanelemojicomplete.h" +#include "lluictrlfactory.h" + +constexpr U32 MIN_MOUSE_MOVE_DELTA = 4; + +// ============================================================================ +// LLPanelEmojiComplete +// + +static LLDefaultChildRegistry::Register<LLPanelEmojiComplete> r("emoji_complete"); + +LLPanelEmojiComplete::Params::Params() +	: autosize("autosize") +	, max_emoji("max_emoji") +	, padding("padding") +	, selected_image("selected_image") +{ +} + +LLPanelEmojiComplete::LLPanelEmojiComplete(const LLPanelEmojiComplete::Params& p) +	: LLUICtrl(p) +	, mAutoSize(p.autosize) +	, mMaxVisible(p.max_emoji) +	, mPadding(p.padding) +	, mSelectedImage(p.selected_image) +{ +	setFont(p.font); +} + +LLPanelEmojiComplete::~LLPanelEmojiComplete() +{ +} + +void LLPanelEmojiComplete::draw() +{ +	if (!mEmojis.empty()) +	{ +		const S32 centerY = mRenderRect.getCenterY(); +		const size_t firstVisibleIdx = mScrollPos, lastVisibleIdx = llmin(mScrollPos + mVisibleEmojis, mEmojis.size()) - 1; + +		if (mCurSelected >= firstVisibleIdx && mCurSelected <= lastVisibleIdx) +		{ +			const S32 emoji_left = mRenderRect.mLeft + (mCurSelected - firstVisibleIdx) * mEmojiWidth; +			const S32 emoji_height = mFont->getLineHeight() + mPadding; +			mSelectedImage->draw(emoji_left, centerY - emoji_height / 2, mEmojiWidth, emoji_height); +		} + +		U32 left = mRenderRect.mLeft + mPadding; +		for (U32 curIdx = firstVisibleIdx; curIdx <= lastVisibleIdx; curIdx++) +		{ +			mFont->render( +				mEmojis, curIdx, +				left, centerY, +				LLColor4::white, LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW_SOFT, +				1, S32_MAX, nullptr, false, true); +			left += mEmojiWidth; +		} +	} +} + +BOOL LLPanelEmojiComplete::handleHover(S32 x, S32 y, MASK mask) +{ +	LLVector2 curHover(x, y); +	if ((mLastHover - curHover).lengthSquared() > MIN_MOUSE_MOVE_DELTA) +	{ +		mCurSelected = posToIndex(x, y); +		mLastHover = curHover; +	} + +	return TRUE; +} + +BOOL LLPanelEmojiComplete::handleKey(KEY key, MASK mask, BOOL called_from_parent) +{ +	bool handled = false; +	if (MASK_NONE == mask) +	{ +		switch (key) +		{ +			case KEY_LEFT: +			case KEY_UP: +				selectPrevious(); +				handled = true; +				break; +			case KEY_RIGHT: +			case KEY_DOWN: +				selectNext(); +				handled = true; +				break; +			case KEY_RETURN: +				if (!mEmojis.empty()) +				{ +					onCommit(); +					handled = true; +				} +				break; +		} +	} + +	if (handled) +	{ +		return TRUE; +	} +	return LLUICtrl::handleKey(key, mask, called_from_parent); +} + +BOOL LLPanelEmojiComplete::handleMouseDown(S32 x, S32 y, MASK mask) +{ +	mCurSelected = posToIndex(x, y); +	mLastHover = LLVector2(x, y); + +	return TRUE; +} + +BOOL LLPanelEmojiComplete::handleMouseUp(S32 x, S32 y, MASK mask) +{ +	mCurSelected = posToIndex(x, y); +	onCommit(); + +	return TRUE; +} + +void LLPanelEmojiComplete::onCommit() +{ +	if (npos != mCurSelected) +	{ +		LLWString wstr; +		wstr.push_back(mEmojis.at(mCurSelected)); +		setValue(wstring_to_utf8str(wstr)); +		LLUICtrl::onCommit(); +	} +} + +void LLPanelEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_parent) +{ +	LLUICtrl::reshape(width, height, called_from_parent); +	updateConstraints(); +} + +void LLPanelEmojiComplete::setEmojiHint(const std::string& hint) +{ +	llwchar curEmoji = (mCurSelected < mEmojis.size()) ? mEmojis.at(mCurSelected) : 0; + +	mEmojis = LLEmojiDictionary::instance().findMatchingEmojis(hint); +	size_t curEmojiIdx = (curEmoji) ? mEmojis.find(curEmoji) : std::string::npos; +	mCurSelected = (std::string::npos != curEmojiIdx) ? curEmojiIdx : 0; + +	if (mAutoSize) +	{ +		mVisibleEmojis = std::min(mEmojis.size(), mMaxVisible); +		reshape(mVisibleEmojis * mEmojiWidth, getRect().getHeight(), false); +	} +	else +	{ +		updateConstraints(); +	} + +	mScrollPos = llmin(mScrollPos, mEmojis.size()); +} + +size_t LLPanelEmojiComplete::posToIndex(S32 x, S32 y) const +{ +	if (mRenderRect.pointInRect(x, y)) +	{ +		return mScrollPos + llmin((size_t)x / mEmojiWidth, mEmojis.size() - 1); +	} +	return npos; +} + +void LLPanelEmojiComplete::select(size_t emoji_idx) +{ +	mCurSelected = llclamp<size_t>(emoji_idx, 0, mEmojis.size()); +	updateScrollPos(); +} + +void LLPanelEmojiComplete::selectNext() +{ +	select(mCurSelected + 1 < mEmojis.size() ? mCurSelected + 1 : 0); +} + +void LLPanelEmojiComplete::selectPrevious() +{ +	select(mCurSelected - 1 >= 0 ? mCurSelected - 1 : mEmojis.size() - 1); +} + +void LLPanelEmojiComplete::setFont(const LLFontGL* fontp) +{ +	mFont = fontp; +	updateConstraints(); +} + +void LLPanelEmojiComplete::updateConstraints() +{ +	const S32 ctrlWidth = getLocalRect().getWidth(); + +	mEmojiWidth = mFont->getWidthF32(u8"\U0001F431") + mPadding * 2; +	mVisibleEmojis = ctrlWidth / mEmojiWidth; +	mRenderRect = getLocalRect().stretch((ctrlWidth - mVisibleEmojis * mEmojiWidth) / -2, 0); + +	updateScrollPos(); +} + +void LLPanelEmojiComplete::updateScrollPos() +{ +	const size_t cntEmoji = mEmojis.size(); +	if (0 == cntEmoji || cntEmoji < mVisibleEmojis || 0 == mCurSelected) +	{ +		mScrollPos = 0; +	} +	else if (cntEmoji - 1 == mCurSelected) +	{ +		mScrollPos = mCurSelected - mVisibleEmojis + 1; +	} +	else +	{ +		mScrollPos = mCurSelected - ((float)mCurSelected / (cntEmoji - 2) * (mVisibleEmojis - 2)); +	} +} + +// ============================================================================ +// LLFloaterEmojiComplete +// + +LLFloaterEmojiComplete::LLFloaterEmojiComplete(const LLSD& sdKey) +	: LLFloater(sdKey) +{ +	// This floater should hover on top of our dependent (with the dependent having the focus) +	setFocusStealsFrontmost(false); +	setAutoFocus(false); +	setBackgroundVisible(false); +	setIsChrome(true); +} + +BOOL LLFloaterEmojiComplete::handleKey(KEY key, MASK mask, BOOL called_from_parent) +{ +	bool handled = false; +	if (MASK_NONE == mask) +	{ +		switch (key) +		{ +			case KEY_ESCAPE: +				LLEmojiHelper::instance().hideHelper(); +				handled = true; +				break; +		} + +	} + +	if (handled) +		return TRUE; +	return LLFloater::handleKey(key, mask, called_from_parent); +} + +void LLFloaterEmojiComplete::onOpen(const LLSD& key) +{ +	mEmojiCtrl->setEmojiHint(key["hint"].asString()); +	if (0 == mEmojiCtrl->getEmojiCount()) +	{ +		LLEmojiHelper::instance().hideHelper(); +	} +} + +BOOL LLFloaterEmojiComplete::postBuild() +{ +	mEmojiCtrl = findChild<LLPanelEmojiComplete>("emoji_complete_ctrl"); +	mEmojiCtrl->setCommitCallback( +		std::bind([&](const LLSD& sdValue) +		{ +			setValue(sdValue); +			onCommit(); +		}, std::placeholders::_2)); +	mEmojiCtrlHorz = getRect().getWidth() - mEmojiCtrl->getRect().getWidth(); + +	return LLFloater::postBuild(); +} + +void LLFloaterEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_parent) +{ +	if (!called_from_parent) +	{ +		LLRect rctFloater = getRect(), rctCtrl = mEmojiCtrl->getRect(); +		rctFloater.mRight = rctFloater.mLeft + rctCtrl.getWidth() + mEmojiCtrlHorz; +		setRect(rctFloater); + +		return; +	} + +	LLFloater::reshape(width, height, called_from_parent); +} + +// ============================================================================ diff --git a/indra/newview/llpanelemojicomplete.h b/indra/newview/llpanelemojicomplete.h new file mode 100644 index 0000000000..2116b350be --- /dev/null +++ b/indra/newview/llpanelemojicomplete.h @@ -0,0 +1,115 @@ +/** +* @file llpanelemojicomplete.h +* @brief Header file for LLPanelEmojiComplete +* +* $LicenseInfo:firstyear=2014&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2014, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ + +#pragma once + +#include "llfloater.h" +#include "lluictrl.h" + +// ============================================================================ +// LLPanelEmojiComplete +// + +class LLPanelEmojiComplete : public LLUICtrl +{ +	friend class LLUICtrlFactory; +public: +	struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> +	{ +		Optional<bool>       autosize; +		Optional<S32>        max_emoji, +		                     padding; + +		Optional<LLUIImage*> selected_image; + +		Params(); +	}; + +protected: +	LLPanelEmojiComplete(const LLPanelEmojiComplete::Params&); +public: +	virtual ~LLPanelEmojiComplete(); + +	void draw() override; +	BOOL handleHover(S32 x, S32 y, MASK mask) override; +	BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent) override; +	BOOL handleMouseDown(S32 x, S32 y, MASK mask) override; +	BOOL handleMouseUp(S32 x, S32 y, MASK mask) override; +	void onCommit() override; +	void reshape(S32 width, S32 height, BOOL called_from_parent) override; + +public: +	size_t getEmojiCount() const { return mEmojis.size(); } +	void   setEmojiHint(const std::string& hint); +protected: +	size_t posToIndex(S32 x, S32 y) const; +	void select(size_t emoji_idx); +	void selectNext(); +	void selectPrevious(); +	void setFont(const LLFontGL* fontp); +	void updateConstraints(); +	void updateScrollPos(); + +protected: +	static constexpr auto npos = std::numeric_limits<size_t>::max(); + +	bool            mAutoSize = false; +	const LLFontGL* mFont; +	U16             mEmojiWidth = 0; +	size_t          mMaxVisible = 0; +	S32             mPadding = 8; +	LLRect          mRenderRect; +	LLUIImagePtr	mSelectedImage; + +	LLWString       mEmojis; +	U16             mVisibleEmojis = 0; +	size_t          mFirstVisible = 0; +	size_t          mScrollPos = 0; +	size_t          mCurSelected = 0; +	LLVector2       mLastHover; +}; + +// ============================================================================ +// LLFloaterEmojiComplete +// + +class LLFloaterEmojiComplete : public LLFloater +{ +public: +	LLFloaterEmojiComplete(const LLSD& sdKey); + +public: +	BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent) override; +	void onOpen(const LLSD& key) override; +	BOOL postBuild() override; +	void reshape(S32 width, S32 height, BOOL called_from_parent) override; + +protected: +	LLPanelEmojiComplete* mEmojiCtrl = nullptr; +	S32                   mEmojiCtrlHorz = 0; +}; + +// ============================================================================ diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index cf9211767e..4431f6527d 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -613,7 +613,7 @@ void LLGLTexMemBar::draw()  	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3,  											 text_color, LLFontGL::LEFT, LLFontGL::TOP,  											 LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, -											 &x_right, FALSE); +											 &x_right, /*use_ellipses*/FALSE, /*use_color*/FALSE);  	F32Kilobits bandwidth(LLAppViewer::getTextureFetch()->getTextureBandwidth());  	F32Kilobits max_bandwidth(gSavedSettings.getF32("ThrottleBandwidthKBPS")); diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 59654350e4..32b32fdc02 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -158,6 +158,7 @@  #include "llfloaterimnearbychat.h"  #include "llpanelblockedlist.h"  #include "llpanelprofileclassifieds.h" +#include "llpanelemojicomplete.h"  #include "llpreviewanim.h"  #include "llpreviewgesture.h"  #include "llpreviewnotecard.h" @@ -244,6 +245,7 @@ void LLViewerFloaterReg::registerFloaters()  	LLFloaterReg::add("delete_pref_preset", "floater_delete_pref_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterDeletePrefPreset>);  	LLFloaterReg::add("destinations", "floater_destinations.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterDestinations>); +	LLFloaterReg::add("emoji_complete", "floater_emoji_complete.xml", &LLFloaterReg::build<LLFloaterEmojiComplete>);  	LLFloaterReg::add("env_post_process", "floater_post_process.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPostProcess>);      LLFloaterReg::add("env_fixed_environmentent_water", "floater_fixedenvironment.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFixedEnvironmentWater>); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index c796ed30f7..6a0cf00a07 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -9568,6 +9568,10 @@ void initialize_menus()  	//Develop (clear cache immediately)  	commit.add("Develop.ClearCache", boost::bind(&handle_cache_clear_immediately) ); +	// Develop (Fonts debugging) +	commit.add("Develop.Fonts.Dump", boost::bind(&LLFontGL::dumpFonts)); +	commit.add("Develop.Fonts.DumpTextures", boost::bind(&LLFontGL::dumpFontTextures)); +  	// Admin >Object  	view_listener_t::addMenu(new LLAdminForceTakeCopy(), "Admin.ForceTakeCopy");  	view_listener_t::addMenu(new LLAdminHandleObjectOwnerSelf(), "Admin.HandleObjectOwnerSelf"); diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index e3ac56d0d3..8a11c5cf8f 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -1118,7 +1118,7 @@ void LLViewerFetchedTexture::init(bool firstinit)  	mLoadedCallbackDesiredDiscardLevel = S8_MAX;  	mPauseLoadedCallBacks = FALSE; -	mNeedsCreateTexture = FALSE; +	mNeedsCreateTexture = false;  	mIsRawImageValid = FALSE;  	mRawDiscardLevel = INVALID_DISCARD_LEVEL; @@ -1400,12 +1400,12 @@ void LLViewerFetchedTexture::addToCreateTexture()  	{  		//just update some variables, not to create a real GL texture.  		createGLTexture(mRawDiscardLevel, mRawImage, 0, FALSE); -		mNeedsCreateTexture = FALSE; +		mNeedsCreateTexture = false;  		destroyRawImage();  	}  	else if(!force_update && getDiscardLevel() > -1 && getDiscardLevel() <= mRawDiscardLevel)  	{ -		mNeedsCreateTexture = FALSE; +		mNeedsCreateTexture = false;  		destroyRawImage();  	}  	else @@ -1441,7 +1441,7 @@ void LLViewerFetchedTexture::addToCreateTexture()  						mRawDiscardLevel += i;  						if(mRawDiscardLevel >= getDiscardLevel() && getDiscardLevel() > 0)  						{ -							mNeedsCreateTexture = FALSE; +							mNeedsCreateTexture = false;  							destroyRawImage();  							return;  						} @@ -1473,7 +1473,7 @@ BOOL LLViewerFetchedTexture::preCreateTexture(S32 usename/*= 0*/)          destroyRawImage();          return FALSE;      } -    mNeedsCreateTexture = FALSE; +    mNeedsCreateTexture = false;      if (mRawImage.isNull())      { @@ -1609,14 +1609,14 @@ void LLViewerFetchedTexture::postCreateTexture()          destroyRawImage();      } -    mNeedsCreateTexture = FALSE; +    mNeedsCreateTexture = false;  }  void LLViewerFetchedTexture::scheduleCreateTexture()  {      if (!mNeedsCreateTexture)      { -        mNeedsCreateTexture = TRUE; +        mNeedsCreateTexture = true;          if (preCreateTexture())          {  #if LL_IMAGEGL_THREAD_CHECK @@ -1630,7 +1630,7 @@ void LLViewerFetchedTexture::scheduleCreateTexture()                  memcpy(data_copy, data, size);              }  #endif -            mNeedsCreateTexture = TRUE; +            mNeedsCreateTexture = true;              auto mainq = LLImageGLThread::sEnabled ? mMainQueue.lock() : nullptr;              if (mainq)              { diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index b953d7006b..2f5e0d01df 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -27,6 +27,7 @@  #ifndef LL_LLVIEWERTEXTURE_H					  #define LL_LLVIEWERTEXTURE_H +#include "llatomic.h"  #include "llgltexture.h"  #include "lltimer.h"  #include "llframetimer.h" @@ -528,7 +529,9 @@ protected:  	LLFrameTimer mStopFetchingTimer;	// Time since mDecodePriority == 0.f.  	BOOL  mInImageList;				// TRUE if image is in list (in which case don't reset priority!) -	BOOL  mNeedsCreateTexture;	 +	// This needs to be atomic, since it is written both in the main thread +	// and in the GL image worker thread... HB +	LLAtomicBool  mNeedsCreateTexture;	  	BOOL   mForSculpt ; //a flag if the texture is used as sculpt data.  	BOOL   mIsFetched ; //is loaded from remote or from cache, not generated locally. diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 3b391e311a..f65deb4577 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1008,7 +1008,7 @@ public:  			const Line& line = *iter;  			LLFontGL::getFontMonospace()->renderUTF8(line.text, 0, (F32)line.x, (F32)line.y, mTextColor,  											 LLFontGL::LEFT, LLFontGL::TOP, -											 LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE); +											 LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, /*use_ellipses*/FALSE, /*use_color*/FALSE);  		}  	} diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp index 6e994b4e68..67632cdbf7 100755 --- a/indra/newview/llworldmapview.cpp +++ b/indra/newview/llworldmapview.cpp @@ -551,7 +551,8 @@ void LLWorldMapView::draw()  					S32_MAX, //max_chars  					mMapScale, //max_pixels  					NULL, -					TRUE); //use ellipses +					/*use_ellipses*/TRUE, +					/*use_color*/FALSE);  			}  		}  	} diff --git a/indra/newview/skins/default/xui/en/emoji_characters.xml b/indra/newview/skins/default/xui/en/emoji_characters.xml new file mode 100644 index 0000000000..55aefe6745 --- /dev/null +++ b/indra/newview/skins/default/xui/en/emoji_characters.xml @@ -0,0 +1,10955 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +	<array> +		<map> +			<key>Character</key> +			<string>😀</string> +			<key>ShortCodes</key> +			<array> +				<string>:grinning:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Smiling</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😄</string> +			<key>ShortCodes</key> +			<array> +				<string>:smile:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Smiling</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😆</string> +			<key>ShortCodes</key> +			<array> +				<string>:laughing:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Smiling</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤣</string> +			<key>ShortCodes</key> +			<array> +				<string>:rofl:</string> +				<string>:satisfied:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Smiling</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙂</string> +			<key>ShortCodes</key> +			<array> +				<string>:slightly_smiling_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Smiling</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😉</string> +			<key>ShortCodes</key> +			<array> +				<string>:wink:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Smiling</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😇</string> +			<key>ShortCodes</key> +			<array> +				<string>:innocent:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Smiling</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😃</string> +			<key>ShortCodes</key> +			<array> +				<string>:smiley:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Smiling</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😁</string> +			<key>ShortCodes</key> +			<array> +				<string>:grin:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Smiling</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😅</string> +			<key>ShortCodes</key> +			<array> +				<string>:sweat_smile:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Smiling</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😂</string> +			<key>ShortCodes</key> +			<array> +				<string>:joy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Smiling</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙃</string> +			<key>ShortCodes</key> +			<array> +				<string>:upside_down_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Smiling</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😊</string> +			<key>ShortCodes</key> +			<array> +				<string>:blush:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Smiling</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥰</string> +			<key>ShortCodes</key> +			<array> +				<string>:smiling_face_with_three_hearts:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Affection</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤩</string> +			<key>ShortCodes</key> +			<array> +				<string>:star_struck:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Affection</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😗</string> +			<key>ShortCodes</key> +			<array> +				<string>:kissing:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Affection</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😚</string> +			<key>ShortCodes</key> +			<array> +				<string>:kissing_closed_eyes:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Affection</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥲</string> +			<key>ShortCodes</key> +			<array> +				<string>:smiling_face_with_tear:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Affection</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😍</string> +			<key>ShortCodes</key> +			<array> +				<string>:heart_eyes:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Affection</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😘</string> +			<key>ShortCodes</key> +			<array> +				<string>:kissing_heart:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Affection</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>☺️</string> +			<key>ShortCodes</key> +			<array> +				<string>:relaxed:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Affection</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😙</string> +			<key>ShortCodes</key> +			<array> +				<string>:kissing_smiling_eyes:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Affection</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😋</string> +			<key>ShortCodes</key> +			<array> +				<string>:yum:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Tongue</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😜</string> +			<key>ShortCodes</key> +			<array> +				<string>:stuck_out_tongue_winking_eye:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Tongue</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😝</string> +			<key>ShortCodes</key> +			<array> +				<string>:stuck_out_tongue_closed_eyes:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Tongue</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😛</string> +			<key>ShortCodes</key> +			<array> +				<string>:stuck_out_tongue:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Tongue</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤪</string> +			<key>ShortCodes</key> +			<array> +				<string>:zany_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Tongue</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤑</string> +			<key>ShortCodes</key> +			<array> +				<string>:money_mouth_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Tongue</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤗</string> +			<key>ShortCodes</key> +			<array> +				<string>:hugs:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Hand</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤫</string> +			<key>ShortCodes</key> +			<array> +				<string>:shushing_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Hand</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤭</string> +			<key>ShortCodes</key> +			<array> +				<string>:hand_over_mouth:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Hand</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤔</string> +			<key>ShortCodes</key> +			<array> +				<string>:thinking:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Hand</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤐</string> +			<key>ShortCodes</key> +			<array> +				<string>:zipper_mouth_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Neutral Skeptical</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😐</string> +			<key>ShortCodes</key> +			<array> +				<string>:neutral_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Neutral Skeptical</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😶</string> +			<key>ShortCodes</key> +			<array> +				<string>:no_mouth:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Neutral Skeptical</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😏</string> +			<key>ShortCodes</key> +			<array> +				<string>:smirk:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Neutral Skeptical</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙄</string> +			<key>ShortCodes</key> +			<array> +				<string>:roll_eyes:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Neutral Skeptical</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😮💨</string> +			<key>ShortCodes</key> +			<array> +				<string>:face_exhaling:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Neutral Skeptical</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤨</string> +			<key>ShortCodes</key> +			<array> +				<string>:raised_eyebrow:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Neutral Skeptical</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😑</string> +			<key>ShortCodes</key> +			<array> +				<string>:expressionless:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Neutral Skeptical</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😶🌫️</string> +			<key>ShortCodes</key> +			<array> +				<string>:face_in_clouds:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Neutral Skeptical</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😒</string> +			<key>ShortCodes</key> +			<array> +				<string>:unamused:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Neutral Skeptical</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😬</string> +			<key>ShortCodes</key> +			<array> +				<string>:grimacing:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Neutral Skeptical</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤥</string> +			<key>ShortCodes</key> +			<array> +				<string>:lying_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Neutral Skeptical</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😌</string> +			<key>ShortCodes</key> +			<array> +				<string>:relieved:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Sleepy</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😪</string> +			<key>ShortCodes</key> +			<array> +				<string>:sleepy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Sleepy</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😴</string> +			<key>ShortCodes</key> +			<array> +				<string>:sleeping:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Sleepy</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😔</string> +			<key>ShortCodes</key> +			<array> +				<string>:pensive:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Sleepy</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤤</string> +			<key>ShortCodes</key> +			<array> +				<string>:drooling_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Sleepy</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😷</string> +			<key>ShortCodes</key> +			<array> +				<string>:mask:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Unwell</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤕</string> +			<key>ShortCodes</key> +			<array> +				<string>:face_with_head_bandage:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Unwell</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤮</string> +			<key>ShortCodes</key> +			<array> +				<string>:vomiting_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Unwell</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥵</string> +			<key>ShortCodes</key> +			<array> +				<string>:hot_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Unwell</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥴</string> +			<key>ShortCodes</key> +			<array> +				<string>:woozy_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Unwell</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😵💫</string> +			<key>ShortCodes</key> +			<array> +				<string>:face_with_spiral_eyes:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Unwell</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤒</string> +			<key>ShortCodes</key> +			<array> +				<string>:face_with_thermometer:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Unwell</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤢</string> +			<key>ShortCodes</key> +			<array> +				<string>:nauseated_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Unwell</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤧</string> +			<key>ShortCodes</key> +			<array> +				<string>:sneezing_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Unwell</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥶</string> +			<key>ShortCodes</key> +			<array> +				<string>:cold_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Unwell</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😵</string> +			<key>ShortCodes</key> +			<array> +				<string>:dizzy_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Unwell</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤯</string> +			<key>ShortCodes</key> +			<array> +				<string>:exploding_head:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Unwell</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤠</string> +			<key>ShortCodes</key> +			<array> +				<string>:cowboy_hat_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Hat</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥸</string> +			<key>ShortCodes</key> +			<array> +				<string>:disguised_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Hat</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥳</string> +			<key>ShortCodes</key> +			<array> +				<string>:partying_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Hat</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😎</string> +			<key>ShortCodes</key> +			<array> +				<string>:sunglasses:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Glasses</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧐</string> +			<key>ShortCodes</key> +			<array> +				<string>:monocle_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Glasses</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤓</string> +			<key>ShortCodes</key> +			<array> +				<string>:nerd_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Glasses</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😕</string> +			<key>ShortCodes</key> +			<array> +				<string>:confused:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙁</string> +			<key>ShortCodes</key> +			<array> +				<string>:slightly_frowning_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😮</string> +			<key>ShortCodes</key> +			<array> +				<string>:open_mouth:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😲</string> +			<key>ShortCodes</key> +			<array> +				<string>:astonished:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥺</string> +			<key>ShortCodes</key> +			<array> +				<string>:pleading_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😧</string> +			<key>ShortCodes</key> +			<array> +				<string>:anguished:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😰</string> +			<key>ShortCodes</key> +			<array> +				<string>:cold_sweat:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😢</string> +			<key>ShortCodes</key> +			<array> +				<string>:cry:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😱</string> +			<key>ShortCodes</key> +			<array> +				<string>:scream:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😣</string> +			<key>ShortCodes</key> +			<array> +				<string>:persevere:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😓</string> +			<key>ShortCodes</key> +			<array> +				<string>:sweat:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😫</string> +			<key>ShortCodes</key> +			<array> +				<string>:tired_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😟</string> +			<key>ShortCodes</key> +			<array> +				<string>:worried:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>☹️</string> +			<key>ShortCodes</key> +			<array> +				<string>:frowning_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😯</string> +			<key>ShortCodes</key> +			<array> +				<string>:hushed:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😳</string> +			<key>ShortCodes</key> +			<array> +				<string>:flushed:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😦</string> +			<key>ShortCodes</key> +			<array> +				<string>:frowning:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😨</string> +			<key>ShortCodes</key> +			<array> +				<string>:fearful:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😥</string> +			<key>ShortCodes</key> +			<array> +				<string>:disappointed_relieved:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😭</string> +			<key>ShortCodes</key> +			<array> +				<string>:sob:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😖</string> +			<key>ShortCodes</key> +			<array> +				<string>:confounded:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😞</string> +			<key>ShortCodes</key> +			<array> +				<string>:disappointed:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😩</string> +			<key>ShortCodes</key> +			<array> +				<string>:weary:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥱</string> +			<key>ShortCodes</key> +			<array> +				<string>:yawning_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Concerned</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😤</string> +			<key>ShortCodes</key> +			<array> +				<string>:triumph:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Negative</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😠</string> +			<key>ShortCodes</key> +			<array> +				<string>:angry:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Negative</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😈</string> +			<key>ShortCodes</key> +			<array> +				<string>:smiling_imp:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Negative</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💀</string> +			<key>ShortCodes</key> +			<array> +				<string>:skull:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Negative</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😡</string> +			<key>ShortCodes</key> +			<array> +				<string>:pout:</string> +				<string>:rage:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Negative</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤬</string> +			<key>ShortCodes</key> +			<array> +				<string>:cursing_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Negative</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👿</string> +			<key>ShortCodes</key> +			<array> +				<string>:imp:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Negative</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>☠️</string> +			<key>ShortCodes</key> +			<array> +				<string>:skull_and_crossbones:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Negative</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💩</string> +			<key>ShortCodes</key> +			<array> +				<string>:hankey:</string> +				<string>:poop:</string> +				<string>:shit:</string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Costume</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👹</string> +			<key>ShortCodes</key> +			<array> +				<string>:japanese_ogre:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Costume</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👻</string> +			<key>ShortCodes</key> +			<array> +				<string>:ghost:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Costume</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👾</string> +			<key>ShortCodes</key> +			<array> +				<string>:space_invader:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Costume</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤡</string> +			<key>ShortCodes</key> +			<array> +				<string>:clown_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Costume</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👺</string> +			<key>ShortCodes</key> +			<array> +				<string>:japanese_goblin:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Costume</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👽</string> +			<key>ShortCodes</key> +			<array> +				<string>:alien:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Costume</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤖</string> +			<key>ShortCodes</key> +			<array> +				<string>:robot:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Face Costume</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😺</string> +			<key>ShortCodes</key> +			<array> +				<string>:smiley_cat:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Cat Face</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😹</string> +			<key>ShortCodes</key> +			<array> +				<string>:joy_cat:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Cat Face</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😼</string> +			<key>ShortCodes</key> +			<array> +				<string>:smirk_cat:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Cat Face</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙀</string> +			<key>ShortCodes</key> +			<array> +				<string>:scream_cat:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Cat Face</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😾</string> +			<key>ShortCodes</key> +			<array> +				<string>:pouting_cat:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Cat Face</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😸</string> +			<key>ShortCodes</key> +			<array> +				<string>:smile_cat:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Cat Face</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😻</string> +			<key>ShortCodes</key> +			<array> +				<string>:heart_eyes_cat:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Cat Face</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😽</string> +			<key>ShortCodes</key> +			<array> +				<string>:kissing_cat:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Cat Face</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>😿</string> +			<key>ShortCodes</key> +			<array> +				<string>:crying_cat_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Cat Face</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙈</string> +			<key>ShortCodes</key> +			<array> +				<string>:see_no_evil:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Monkey Face</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙊</string> +			<key>ShortCodes</key> +			<array> +				<string>:speak_no_evil:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Monkey Face</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙉</string> +			<key>ShortCodes</key> +			<array> +				<string>:hear_no_evil:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Smiley</string> +				<string>Monkey Face</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💋</string> +			<key>ShortCodes</key> +			<array> +				<string>:kiss:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💘</string> +			<key>ShortCodes</key> +			<array> +				<string>:cupid:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💖</string> +			<key>ShortCodes</key> +			<array> +				<string>:sparkling_heart:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💓</string> +			<key>ShortCodes</key> +			<array> +				<string>:heartbeat:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💕</string> +			<key>ShortCodes</key> +			<array> +				<string>:two_hearts:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>❣️</string> +			<key>ShortCodes</key> +			<array> +				<string>:heavy_heart_exclamation:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>❤️🔥</string> +			<key>ShortCodes</key> +			<array> +				<string>:heart_on_fire:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>❤️</string> +			<key>ShortCodes</key> +			<array> +				<string>:heart:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💛</string> +			<key>ShortCodes</key> +			<array> +				<string>:yellow_heart:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💙</string> +			<key>ShortCodes</key> +			<array> +				<string>:blue_heart:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤎</string> +			<key>ShortCodes</key> +			<array> +				<string>:brown_heart:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤍</string> +			<key>ShortCodes</key> +			<array> +				<string>:white_heart:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💢</string> +			<key>ShortCodes</key> +			<array> +				<string>:anger:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💫</string> +			<key>ShortCodes</key> +			<array> +				<string>:dizzy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💨</string> +			<key>ShortCodes</key> +			<array> +				<string>:dash:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💣</string> +			<key>ShortCodes</key> +			<array> +				<string>:bomb:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👁️🗨️</string> +			<key>ShortCodes</key> +			<array> +				<string>:eye_speech_bubble:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🗯️</string> +			<key>ShortCodes</key> +			<array> +				<string>:right_anger_bubble:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💤</string> +			<key>ShortCodes</key> +			<array> +				<string>:zzz:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💌</string> +			<key>ShortCodes</key> +			<array> +				<string>:love_letter:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💝</string> +			<key>ShortCodes</key> +			<array> +				<string>:gift_heart:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💗</string> +			<key>ShortCodes</key> +			<array> +				<string>:heartpulse:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💞</string> +			<key>ShortCodes</key> +			<array> +				<string>:revolving_hearts:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💟</string> +			<key>ShortCodes</key> +			<array> +				<string>:heart_decoration:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💔</string> +			<key>ShortCodes</key> +			<array> +				<string>:broken_heart:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>❤️🩹</string> +			<key>ShortCodes</key> +			<array> +				<string>:mending_heart:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧡</string> +			<key>ShortCodes</key> +			<array> +				<string>:orange_heart:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💚</string> +			<key>ShortCodes</key> +			<array> +				<string>:green_heart:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💜</string> +			<key>ShortCodes</key> +			<array> +				<string>:purple_heart:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🖤</string> +			<key>ShortCodes</key> +			<array> +				<string>:black_heart:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💯</string> +			<key>ShortCodes</key> +			<array> +				<string>:100:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💥</string> +			<key>ShortCodes</key> +			<array> +				<string>:boom:</string> +				<string>:collision:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💦</string> +			<key>ShortCodes</key> +			<array> +				<string>:sweat_drops:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🕳️</string> +			<key>ShortCodes</key> +			<array> +				<string>:hole:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💬</string> +			<key>ShortCodes</key> +			<array> +				<string>:speech_balloon:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🗨️</string> +			<key>ShortCodes</key> +			<array> +				<string>:left_speech_bubble:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💭</string> +			<key>ShortCodes</key> +			<array> +				<string>:thought_balloon:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Emotion</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👋</string> +			<key>ShortCodes</key> +			<array> +				<string>:wave:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Open</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🖐️</string> +			<key>ShortCodes</key> +			<array> +				<string>:raised_hand_with_fingers_splayed:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Open</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🖖</string> +			<key>ShortCodes</key> +			<array> +				<string>:vulcan_salute:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Open</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤚</string> +			<key>ShortCodes</key> +			<array> +				<string>:raised_back_of_hand:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Open</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>✋</string> +			<key>ShortCodes</key> +			<array> +				<string>:hand:</string> +				<string>:raised_hand:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Open</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👌</string> +			<key>ShortCodes</key> +			<array> +				<string>:ok_hand:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Partial</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤏</string> +			<key>ShortCodes</key> +			<array> +				<string>:pinching_hand:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Partial</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤞</string> +			<key>ShortCodes</key> +			<array> +				<string>:crossed_fingers:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Partial</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤘</string> +			<key>ShortCodes</key> +			<array> +				<string>:metal:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Partial</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤌</string> +			<key>ShortCodes</key> +			<array> +				<string>:pinched_fingers:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Partial</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>✌️</string> +			<key>ShortCodes</key> +			<array> +				<string>:v:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Partial</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤟</string> +			<key>ShortCodes</key> +			<array> +				<string>:love_you_gesture:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Partial</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤙</string> +			<key>ShortCodes</key> +			<array> +				<string>:call_me_hand:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Partial</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👈</string> +			<key>ShortCodes</key> +			<array> +				<string>:point_left:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Single Finger</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👆</string> +			<key>ShortCodes</key> +			<array> +				<string>:point_up_2:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Single Finger</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👇</string> +			<key>ShortCodes</key> +			<array> +				<string>:point_down:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Single Finger</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👉</string> +			<key>ShortCodes</key> +			<array> +				<string>:point_right:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Single Finger</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🖕</string> +			<key>ShortCodes</key> +			<array> +				<string>:fu:</string> +				<string>:middle_finger:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Single Finger</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>☝️</string> +			<key>ShortCodes</key> +			<array> +				<string>:point_up:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Single Finger</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👍</string> +			<key>ShortCodes</key> +			<array> +				<string>:+1:</string> +				<string>:thumbsup:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Closed</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>✊</string> +			<key>ShortCodes</key> +			<array> +				<string>:fist:</string> +				<string>:fist_raised:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Closed</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤛</string> +			<key>ShortCodes</key> +			<array> +				<string>:fist_left:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Closed</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👎</string> +			<key>ShortCodes</key> +			<array> +				<string>:-1:</string> +				<string>:thumbsdown:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Closed</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👊</string> +			<key>ShortCodes</key> +			<array> +				<string>:facepunch:</string> +				<string>:fist_oncoming:</string> +				<string>:punch:</string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Closed</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤜</string> +			<key>ShortCodes</key> +			<array> +				<string>:fist_right:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Fingers Closed</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👏</string> +			<key>ShortCodes</key> +			<array> +				<string>:clap:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hands</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👐</string> +			<key>ShortCodes</key> +			<array> +				<string>:open_hands:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hands</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤝</string> +			<key>ShortCodes</key> +			<array> +				<string>:handshake:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hands</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙌</string> +			<key>ShortCodes</key> +			<array> +				<string>:raised_hands:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hands</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤲</string> +			<key>ShortCodes</key> +			<array> +				<string>:palms_up_together:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hands</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙏</string> +			<key>ShortCodes</key> +			<array> +				<string>:pray:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hands</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>✍️</string> +			<key>ShortCodes</key> +			<array> +				<string>:writing_hand:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Prop</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤳</string> +			<key>ShortCodes</key> +			<array> +				<string>:selfie:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Prop</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💅</string> +			<key>ShortCodes</key> +			<array> +				<string>:nail_care:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Hand Prop</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💪</string> +			<key>ShortCodes</key> +			<array> +				<string>:muscle:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Body Parts</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦿</string> +			<key>ShortCodes</key> +			<array> +				<string>:mechanical_leg:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Body Parts</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦶</string> +			<key>ShortCodes</key> +			<array> +				<string>:foot:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Body Parts</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦻</string> +			<key>ShortCodes</key> +			<array> +				<string>:ear_with_hearing_aid:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Body Parts</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧠</string> +			<key>ShortCodes</key> +			<array> +				<string>:brain:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Body Parts</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🫁</string> +			<key>ShortCodes</key> +			<array> +				<string>:lungs:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Body Parts</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦴</string> +			<key>ShortCodes</key> +			<array> +				<string>:bone:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Body Parts</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👁️</string> +			<key>ShortCodes</key> +			<array> +				<string>:eye:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Body Parts</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👄</string> +			<key>ShortCodes</key> +			<array> +				<string>:lips:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Body Parts</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦾</string> +			<key>ShortCodes</key> +			<array> +				<string>:mechanical_arm:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Body Parts</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦵</string> +			<key>ShortCodes</key> +			<array> +				<string>:leg:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Body Parts</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👂</string> +			<key>ShortCodes</key> +			<array> +				<string>:ear:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Body Parts</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👃</string> +			<key>ShortCodes</key> +			<array> +				<string>:nose:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Body Parts</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🫀</string> +			<key>ShortCodes</key> +			<array> +				<string>:anatomical_heart:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Body Parts</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦷</string> +			<key>ShortCodes</key> +			<array> +				<string>:tooth:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Body Parts</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👀</string> +			<key>ShortCodes</key> +			<array> +				<string>:eyes:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Body Parts</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👅</string> +			<key>ShortCodes</key> +			<array> +				<string>:tongue:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Body</string> +				<string>Body Parts</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👶</string> +			<key>ShortCodes</key> +			<array> +				<string>:baby:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👦</string> +			<key>ShortCodes</key> +			<array> +				<string>:boy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑</string> +			<key>ShortCodes</key> +			<array> +				<string>:adult:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨</string> +			<key>ShortCodes</key> +			<array> +				<string>:man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧔♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_beard:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🦰</string> +			<key>ShortCodes</key> +			<array> +				<string>:red_haired_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🦳</string> +			<key>ShortCodes</key> +			<array> +				<string>:white_haired_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🦰</string> +			<key>ShortCodes</key> +			<array> +				<string>:person_red_hair:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🦱</string> +			<key>ShortCodes</key> +			<array> +				<string>:person_curly_hair:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🦳</string> +			<key>ShortCodes</key> +			<array> +				<string>:person_white_hair:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🦲</string> +			<key>ShortCodes</key> +			<array> +				<string>:person_bald:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👱♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:blond_haired_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👴</string> +			<key>ShortCodes</key> +			<array> +				<string>:older_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧒</string> +			<key>ShortCodes</key> +			<array> +				<string>:child:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👧</string> +			<key>ShortCodes</key> +			<array> +				<string>:girl:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👱</string> +			<key>ShortCodes</key> +			<array> +				<string>:blond_haired_person:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧔</string> +			<key>ShortCodes</key> +			<array> +				<string>:bearded_person:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧔♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_beard:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🦱</string> +			<key>ShortCodes</key> +			<array> +				<string>:curly_haired_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🦲</string> +			<key>ShortCodes</key> +			<array> +				<string>:bald_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🦰</string> +			<key>ShortCodes</key> +			<array> +				<string>:red_haired_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🦱</string> +			<key>ShortCodes</key> +			<array> +				<string>:curly_haired_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🦳</string> +			<key>ShortCodes</key> +			<array> +				<string>:white_haired_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🦲</string> +			<key>ShortCodes</key> +			<array> +				<string>:bald_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👱♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:blond_haired_woman:</string> +				<string>:blonde_woman:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧓</string> +			<key>ShortCodes</key> +			<array> +				<string>:older_adult:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👵</string> +			<key>ShortCodes</key> +			<array> +				<string>:older_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙍</string> +			<key>ShortCodes</key> +			<array> +				<string>:frowning_person:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙍♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:frowning_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙎♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:pouting_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙅</string> +			<key>ShortCodes</key> +			<array> +				<string>:no_good:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙅♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:ng_woman:</string> +				<string>:no_good_woman:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙆♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:ok_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💁</string> +			<key>ShortCodes</key> +			<array> +				<string>:information_desk_person:</string> +				<string>:tipping_hand_person:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💁♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:sassy_woman:</string> +				<string>:tipping_hand_woman:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙋♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:raising_hand_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧏</string> +			<key>ShortCodes</key> +			<array> +				<string>:deaf_person:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧏♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:deaf_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙇♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:bowing_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤦</string> +			<key>ShortCodes</key> +			<array> +				<string>:facepalm:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤦♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_facepalming:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤷♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_shrugging:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙍♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:frowning_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙎</string> +			<key>ShortCodes</key> +			<array> +				<string>:pouting_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙎♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:pouting_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙅♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:ng_man:</string> +				<string>:no_good_man:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙆</string> +			<key>ShortCodes</key> +			<array> +				<string>:ok_person:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙆♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:ok_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💁♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:sassy_man:</string> +				<string>:tipping_hand_man:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙋</string> +			<key>ShortCodes</key> +			<array> +				<string>:raising_hand:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙋♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:raising_hand_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧏♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:deaf_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙇</string> +			<key>ShortCodes</key> +			<array> +				<string>:bow:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🙇♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:bowing_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤦♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_facepalming:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤷</string> +			<key>ShortCodes</key> +			<array> +				<string>:shrug:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤷♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_shrugging:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Gesture</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑⚕️</string> +			<key>ShortCodes</key> +			<array> +				<string>:health_worker:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩⚕️</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_health_worker:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🎓</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_student:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🏫</string> +			<key>ShortCodes</key> +			<array> +				<string>:teacher:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🏫</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_teacher:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨⚖️</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_judge:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🌾</string> +			<key>ShortCodes</key> +			<array> +				<string>:farmer:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🌾</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_farmer:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🍳</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_cook:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🔧</string> +			<key>ShortCodes</key> +			<array> +				<string>:mechanic:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🔧</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_mechanic:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🏭</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_factory_worker:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑💼</string> +			<key>ShortCodes</key> +			<array> +				<string>:office_worker:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩💼</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_office_worker:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🔬</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_scientist:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑💻</string> +			<key>ShortCodes</key> +			<array> +				<string>:technologist:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩💻</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_technologist:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🎤</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_singer:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🎨</string> +			<key>ShortCodes</key> +			<array> +				<string>:artist:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🎨</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_artist:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨✈️</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_pilot:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🚀</string> +			<key>ShortCodes</key> +			<array> +				<string>:astronaut:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🚀</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_astronaut:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🚒</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_firefighter:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👮</string> +			<key>ShortCodes</key> +			<array> +				<string>:cop:</string> +				<string>:police_officer:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👮♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:policewoman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🕵️♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:male_detective:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💂</string> +			<key>ShortCodes</key> +			<array> +				<string>:guard:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💂♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:guardswoman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👷</string> +			<key>ShortCodes</key> +			<array> +				<string>:construction_worker:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👷♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:construction_worker_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👸</string> +			<key>ShortCodes</key> +			<array> +				<string>:princess:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👳♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_with_turban:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👲</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_with_gua_pi_mao:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤵</string> +			<key>ShortCodes</key> +			<array> +				<string>:person_in_tuxedo:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤵♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_in_tuxedo:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👰♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_with_veil:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤰</string> +			<key>ShortCodes</key> +			<array> +				<string>:pregnant_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🍼</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_feeding_baby:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🍼</string> +			<key>ShortCodes</key> +			<array> +				<string>:person_feeding_baby:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨⚕️</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_health_worker:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🎓</string> +			<key>ShortCodes</key> +			<array> +				<string>:student:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🎓</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_student:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🏫</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_teacher:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑⚖️</string> +			<key>ShortCodes</key> +			<array> +				<string>:judge:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩⚖️</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_judge:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🌾</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_farmer:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🍳</string> +			<key>ShortCodes</key> +			<array> +				<string>:cook:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🍳</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_cook:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🔧</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_mechanic:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🏭</string> +			<key>ShortCodes</key> +			<array> +				<string>:factory_worker:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🏭</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_factory_worker:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨💼</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_office_worker:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🔬</string> +			<key>ShortCodes</key> +			<array> +				<string>:scientist:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🔬</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_scientist:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨💻</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_technologist:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🎤</string> +			<key>ShortCodes</key> +			<array> +				<string>:singer:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🎤</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_singer:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🎨</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_artist:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑✈️</string> +			<key>ShortCodes</key> +			<array> +				<string>:pilot:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩✈️</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_pilot:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🚀</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_astronaut:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🚒</string> +			<key>ShortCodes</key> +			<array> +				<string>:firefighter:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🚒</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_firefighter:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👮♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:policeman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🕵️</string> +			<key>ShortCodes</key> +			<array> +				<string>:detective:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🕵️♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:female_detective:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💂♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:guardsman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥷</string> +			<key>ShortCodes</key> +			<array> +				<string>:ninja:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👷♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:construction_worker_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤴</string> +			<key>ShortCodes</key> +			<array> +				<string>:prince:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👳</string> +			<key>ShortCodes</key> +			<array> +				<string>:person_with_turban:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👳♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_with_turban:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧕</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_with_headscarf:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤵♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_in_tuxedo:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👰</string> +			<key>ShortCodes</key> +			<array> +				<string>:person_with_veil:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👰♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:bride_with_veil:</string> +				<string>:woman_with_veil:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤱</string> +			<key>ShortCodes</key> +			<array> +				<string>:breast_feeding:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🍼</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_feeding_baby:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Role</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👼</string> +			<key>ShortCodes</key> +			<array> +				<string>:angel:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤶</string> +			<key>ShortCodes</key> +			<array> +				<string>:mrs_claus:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦸</string> +			<key>ShortCodes</key> +			<array> +				<string>:superhero:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦸♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:superhero_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦹♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:supervillain_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧙</string> +			<key>ShortCodes</key> +			<array> +				<string>:mage:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧙♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:mage_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧚♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:fairy_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧛</string> +			<key>ShortCodes</key> +			<array> +				<string>:vampire:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧛♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:vampire_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧜♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:merman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧝</string> +			<key>ShortCodes</key> +			<array> +				<string>:elf:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧝♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:elf_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧞♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:genie_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧟</string> +			<key>ShortCodes</key> +			<array> +				<string>:zombie:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧟♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:zombie_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🎅</string> +			<key>ShortCodes</key> +			<array> +				<string>:santa:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🎄</string> +			<key>ShortCodes</key> +			<array> +				<string>:mx_claus:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦸♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:superhero_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦹</string> +			<key>ShortCodes</key> +			<array> +				<string>:supervillain:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦹♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:supervillain_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧙♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:mage_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧚</string> +			<key>ShortCodes</key> +			<array> +				<string>:fairy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧚♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:fairy_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧛♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:vampire_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧜</string> +			<key>ShortCodes</key> +			<array> +				<string>:merperson:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧜♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:mermaid:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧝♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:elf_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧞</string> +			<key>ShortCodes</key> +			<array> +				<string>:genie:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧞♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:genie_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧟♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:zombie_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Fantasy</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💆</string> +			<key>ShortCodes</key> +			<array> +				<string>:massage:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💆♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:massage_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💇♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:haircut_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🚶</string> +			<key>ShortCodes</key> +			<array> +				<string>:walking:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🚶♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:walking_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧍♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:standing_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧎</string> +			<key>ShortCodes</key> +			<array> +				<string>:kneeling_person:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧎♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:kneeling_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🦯</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_with_probing_cane:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🦼</string> +			<key>ShortCodes</key> +			<array> +				<string>:person_in_motorized_wheelchair:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🦼</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_in_motorized_wheelchair:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🦽</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_in_manual_wheelchair:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🏃</string> +			<key>ShortCodes</key> +			<array> +				<string>:runner:</string> +				<string>:running:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🏃♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:running_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🕺</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_dancing:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👯</string> +			<key>ShortCodes</key> +			<array> +				<string>:dancers:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👯♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:dancing_women:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧖♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:sauna_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧗</string> +			<key>ShortCodes</key> +			<array> +				<string>:climbing:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧗♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:climbing_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💆♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:massage_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💇</string> +			<key>ShortCodes</key> +			<array> +				<string>:haircut:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💇♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:haircut_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🚶♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:walking_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧍</string> +			<key>ShortCodes</key> +			<array> +				<string>:standing_person:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧍♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:standing_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧎♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:kneeling_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🦯</string> +			<key>ShortCodes</key> +			<array> +				<string>:person_with_probing_cane:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🦯</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_with_probing_cane:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨🦼</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_in_motorized_wheelchair:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🦽</string> +			<key>ShortCodes</key> +			<array> +				<string>:person_in_manual_wheelchair:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩🦽</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_in_manual_wheelchair:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🏃♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:running_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💃</string> +			<key>ShortCodes</key> +			<array> +				<string>:dancer:</string> +				<string>:woman_dancing:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🕴️</string> +			<key>ShortCodes</key> +			<array> +				<string>:business_suit_levitating:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👯♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:dancing_men:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧖</string> +			<key>ShortCodes</key> +			<array> +				<string>:sauna_person:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧖♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:sauna_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧗♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:climbing_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Activity</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤺</string> +			<key>ShortCodes</key> +			<array> +				<string>:person_fencing:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>⛷️</string> +			<key>ShortCodes</key> +			<array> +				<string>:skier:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🏌️</string> +			<key>ShortCodes</key> +			<array> +				<string>:golfing:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🏌️♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:golfing_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🏄♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:surfing_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🚣</string> +			<key>ShortCodes</key> +			<array> +				<string>:rowboat:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🚣♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:rowing_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🏊♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:swimming_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>⛹️</string> +			<key>ShortCodes</key> +			<array> +				<string>:bouncing_ball_person:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>⛹️♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:basketball_woman:</string> +				<string>:bouncing_ball_woman:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🏋️♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:weight_lifting_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🚴</string> +			<key>ShortCodes</key> +			<array> +				<string>:bicyclist:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🚴♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:biking_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🚵♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:mountain_biking_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤸</string> +			<key>ShortCodes</key> +			<array> +				<string>:cartwheeling:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤸♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_cartwheeling:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤼♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:men_wrestling:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤽</string> +			<key>ShortCodes</key> +			<array> +				<string>:water_polo:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤽♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_playing_water_polo:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤾♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_playing_handball:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤹</string> +			<key>ShortCodes</key> +			<array> +				<string>:juggling_person:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤹♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_juggling:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🏇</string> +			<key>ShortCodes</key> +			<array> +				<string>:horse_racing:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🏂</string> +			<key>ShortCodes</key> +			<array> +				<string>:snowboarder:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🏌️♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:golfing_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🏄</string> +			<key>ShortCodes</key> +			<array> +				<string>:surfer:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🏄♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:surfing_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🚣♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:rowing_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🏊</string> +			<key>ShortCodes</key> +			<array> +				<string>:swimmer:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🏊♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:swimming_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>⛹️♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:basketball_man:</string> +				<string>:bouncing_ball_man:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🏋️</string> +			<key>ShortCodes</key> +			<array> +				<string>:weight_lifting:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🏋️♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:weight_lifting_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🚴♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:biking_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🚵</string> +			<key>ShortCodes</key> +			<array> +				<string>:mountain_bicyclist:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🚵♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:mountain_biking_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤸♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_cartwheeling:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤼</string> +			<key>ShortCodes</key> +			<array> +				<string>:wrestling:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤼♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:women_wrestling:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤽♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_playing_water_polo:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤾</string> +			<key>ShortCodes</key> +			<array> +				<string>:handball_person:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤾♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:woman_playing_handball:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🤹♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:man_juggling:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Sport</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧘</string> +			<key>ShortCodes</key> +			<array> +				<string>:lotus_position:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Resting</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧘♀️</string> +			<key>ShortCodes</key> +			<array> +				<string>:lotus_position_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Resting</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🛌</string> +			<key>ShortCodes</key> +			<array> +				<string>:sleeping_bed:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Resting</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧘♂️</string> +			<key>ShortCodes</key> +			<array> +				<string>:lotus_position_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Resting</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🛀</string> +			<key>ShortCodes</key> +			<array> +				<string>:bath:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Resting</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧑🤝🧑</string> +			<key>ShortCodes</key> +			<array> +				<string>:people_holding_hands:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👫</string> +			<key>ShortCodes</key> +			<array> +				<string>:couple:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💏</string> +			<key>ShortCodes</key> +			<array> +				<string>:couplekiss:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨❤️💋👨</string> +			<key>ShortCodes</key> +			<array> +				<string>:couplekiss_man_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💑</string> +			<key>ShortCodes</key> +			<array> +				<string>:couple_with_heart:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨❤️👨</string> +			<key>ShortCodes</key> +			<array> +				<string>:couple_with_heart_man_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👪</string> +			<key>ShortCodes</key> +			<array> +				<string>:family:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨👩👧</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_man_woman_girl:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨👩👦👦</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_man_woman_boy_boy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨👨👦</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_man_man_boy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨👨👧👦</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_man_man_girl_boy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨👨👧👧</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_man_man_girl_girl:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩👩👧</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_woman_woman_girl:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩👩👦👦</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_woman_woman_boy_boy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨👦</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_man_boy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨👧</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_man_girl:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨👧👧</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_man_girl_girl:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩👦👦</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_woman_boy_boy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩👧👦</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_woman_girl_boy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👭</string> +			<key>ShortCodes</key> +			<array> +				<string>:two_women_holding_hands:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👬</string> +			<key>ShortCodes</key> +			<array> +				<string>:two_men_holding_hands:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩❤️💋👨</string> +			<key>ShortCodes</key> +			<array> +				<string>:couplekiss_man_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩❤️💋👩</string> +			<key>ShortCodes</key> +			<array> +				<string>:couplekiss_woman_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩❤️👨</string> +			<key>ShortCodes</key> +			<array> +				<string>:couple_with_heart_woman_man:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩❤️👩</string> +			<key>ShortCodes</key> +			<array> +				<string>:couple_with_heart_woman_woman:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨👩👦</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_man_woman_boy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨👩👧👦</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_man_woman_girl_boy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨👩👧👧</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_man_woman_girl_girl:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨👨👧</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_man_man_girl:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨👨👦👦</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_man_man_boy_boy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩👩👦</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_woman_woman_boy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩👩👧👦</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_woman_woman_girl_boy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩👩👧👧</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_woman_woman_girl_girl:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨👦👦</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_man_boy_boy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👨👧👦</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_man_girl_boy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩👦</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_woman_boy:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩👧</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_woman_girl:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👩👧👧</string> +			<key>ShortCodes</key> +			<array> +				<string>:family_woman_girl_girl:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person</string> +				<string>Family</string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🗣️</string> +			<key>ShortCodes</key> +			<array> +				<string>:speaking_head:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Symbol</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👥</string> +			<key>ShortCodes</key> +			<array> +				<string>:busts_in_silhouette:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Symbol</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👣</string> +			<key>ShortCodes</key> +			<array> +				<string>:footprints:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Symbol</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>👤</string> +			<key>ShortCodes</key> +			<array> +				<string>:bust_in_silhouette:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Symbol</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🫂</string> +			<key>ShortCodes</key> +			<array> +				<string>:people_hugging:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Person Symbol</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐵</string> +			<key>ShortCodes</key> +			<array> +				<string>:monkey_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦍</string> +			<key>ShortCodes</key> +			<array> +				<string>:gorilla:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐶</string> +			<key>ShortCodes</key> +			<array> +				<string>:dog:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦮</string> +			<key>ShortCodes</key> +			<array> +				<string>:guide_dog:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐩</string> +			<key>ShortCodes</key> +			<array> +				<string>:poodle:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦊</string> +			<key>ShortCodes</key> +			<array> +				<string>:fox_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐱</string> +			<key>ShortCodes</key> +			<array> +				<string>:cat:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐈⬛</string> +			<key>ShortCodes</key> +			<array> +				<string>:black_cat:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐯</string> +			<key>ShortCodes</key> +			<array> +				<string>:tiger:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐆</string> +			<key>ShortCodes</key> +			<array> +				<string>:leopard:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐎</string> +			<key>ShortCodes</key> +			<array> +				<string>:racehorse:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦓</string> +			<key>ShortCodes</key> +			<array> +				<string>:zebra:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦬</string> +			<key>ShortCodes</key> +			<array> +				<string>:bison:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐂</string> +			<key>ShortCodes</key> +			<array> +				<string>:ox:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐄</string> +			<key>ShortCodes</key> +			<array> +				<string>:cow2:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐖</string> +			<key>ShortCodes</key> +			<array> +				<string>:pig2:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐽</string> +			<key>ShortCodes</key> +			<array> +				<string>:pig_nose:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐑</string> +			<key>ShortCodes</key> +			<array> +				<string>:sheep:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐪</string> +			<key>ShortCodes</key> +			<array> +				<string>:dromedary_camel:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦙</string> +			<key>ShortCodes</key> +			<array> +				<string>:llama:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐘</string> +			<key>ShortCodes</key> +			<array> +				<string>:elephant:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦏</string> +			<key>ShortCodes</key> +			<array> +				<string>:rhinoceros:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐭</string> +			<key>ShortCodes</key> +			<array> +				<string>:mouse:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐀</string> +			<key>ShortCodes</key> +			<array> +				<string>:rat:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐰</string> +			<key>ShortCodes</key> +			<array> +				<string>:rabbit:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐿️</string> +			<key>ShortCodes</key> +			<array> +				<string>:chipmunk:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦔</string> +			<key>ShortCodes</key> +			<array> +				<string>:hedgehog:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐻</string> +			<key>ShortCodes</key> +			<array> +				<string>:bear:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐨</string> +			<key>ShortCodes</key> +			<array> +				<string>:koala:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦥</string> +			<key>ShortCodes</key> +			<array> +				<string>:sloth:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦨</string> +			<key>ShortCodes</key> +			<array> +				<string>:skunk:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦡</string> +			<key>ShortCodes</key> +			<array> +				<string>:badger:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐒</string> +			<key>ShortCodes</key> +			<array> +				<string>:monkey:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦧</string> +			<key>ShortCodes</key> +			<array> +				<string>:orangutan:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐕</string> +			<key>ShortCodes</key> +			<array> +				<string>:dog2:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐕🦺</string> +			<key>ShortCodes</key> +			<array> +				<string>:service_dog:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐺</string> +			<key>ShortCodes</key> +			<array> +				<string>:wolf:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦝</string> +			<key>ShortCodes</key> +			<array> +				<string>:raccoon:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐈</string> +			<key>ShortCodes</key> +			<array> +				<string>:cat2:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦁</string> +			<key>ShortCodes</key> +			<array> +				<string>:lion:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐅</string> +			<key>ShortCodes</key> +			<array> +				<string>:tiger2:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐴</string> +			<key>ShortCodes</key> +			<array> +				<string>:horse:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦄</string> +			<key>ShortCodes</key> +			<array> +				<string>:unicorn:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦌</string> +			<key>ShortCodes</key> +			<array> +				<string>:deer:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐮</string> +			<key>ShortCodes</key> +			<array> +				<string>:cow:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐃</string> +			<key>ShortCodes</key> +			<array> +				<string>:water_buffalo:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐷</string> +			<key>ShortCodes</key> +			<array> +				<string>:pig:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐗</string> +			<key>ShortCodes</key> +			<array> +				<string>:boar:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐏</string> +			<key>ShortCodes</key> +			<array> +				<string>:ram:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐐</string> +			<key>ShortCodes</key> +			<array> +				<string>:goat:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐫</string> +			<key>ShortCodes</key> +			<array> +				<string>:camel:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦒</string> +			<key>ShortCodes</key> +			<array> +				<string>:giraffe:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦣</string> +			<key>ShortCodes</key> +			<array> +				<string>:mammoth:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦛</string> +			<key>ShortCodes</key> +			<array> +				<string>:hippopotamus:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐁</string> +			<key>ShortCodes</key> +			<array> +				<string>:mouse2:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐹</string> +			<key>ShortCodes</key> +			<array> +				<string>:hamster:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐇</string> +			<key>ShortCodes</key> +			<array> +				<string>:rabbit2:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦫</string> +			<key>ShortCodes</key> +			<array> +				<string>:beaver:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦇</string> +			<key>ShortCodes</key> +			<array> +				<string>:bat:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐻❄️</string> +			<key>ShortCodes</key> +			<array> +				<string>:polar_bear:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐼</string> +			<key>ShortCodes</key> +			<array> +				<string>:panda_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦦</string> +			<key>ShortCodes</key> +			<array> +				<string>:otter:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦘</string> +			<key>ShortCodes</key> +			<array> +				<string>:kangaroo:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐾</string> +			<key>ShortCodes</key> +			<array> +				<string>:feet:</string> +				<string>:paw_prints:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Mammal</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦃</string> +			<key>ShortCodes</key> +			<array> +				<string>:turkey:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bird</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐓</string> +			<key>ShortCodes</key> +			<array> +				<string>:rooster:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bird</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐤</string> +			<key>ShortCodes</key> +			<array> +				<string>:baby_chick:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bird</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐦</string> +			<key>ShortCodes</key> +			<array> +				<string>:bird:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bird</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🕊️</string> +			<key>ShortCodes</key> +			<array> +				<string>:dove:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bird</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦆</string> +			<key>ShortCodes</key> +			<array> +				<string>:duck:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bird</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦉</string> +			<key>ShortCodes</key> +			<array> +				<string>:owl:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bird</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🪶</string> +			<key>ShortCodes</key> +			<array> +				<string>:feather:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bird</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦚</string> +			<key>ShortCodes</key> +			<array> +				<string>:peacock:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bird</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐔</string> +			<key>ShortCodes</key> +			<array> +				<string>:chicken:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bird</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐣</string> +			<key>ShortCodes</key> +			<array> +				<string>:hatching_chick:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bird</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐥</string> +			<key>ShortCodes</key> +			<array> +				<string>:hatched_chick:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bird</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐧</string> +			<key>ShortCodes</key> +			<array> +				<string>:penguin:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bird</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦅</string> +			<key>ShortCodes</key> +			<array> +				<string>:eagle:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bird</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦢</string> +			<key>ShortCodes</key> +			<array> +				<string>:swan:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bird</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦤</string> +			<key>ShortCodes</key> +			<array> +				<string>:dodo:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bird</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦩</string> +			<key>ShortCodes</key> +			<array> +				<string>:flamingo:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bird</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦜</string> +			<key>ShortCodes</key> +			<array> +				<string>:parrot:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bird</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐸</string> +			<key>ShortCodes</key> +			<array> +				<string>:frog:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Amphibian</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐊</string> +			<key>ShortCodes</key> +			<array> +				<string>:crocodile:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Reptile</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦎</string> +			<key>ShortCodes</key> +			<array> +				<string>:lizard:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Reptile</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐲</string> +			<key>ShortCodes</key> +			<array> +				<string>:dragon_face:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Reptile</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦕</string> +			<key>ShortCodes</key> +			<array> +				<string>:sauropod:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Reptile</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐢</string> +			<key>ShortCodes</key> +			<array> +				<string>:turtle:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Reptile</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐍</string> +			<key>ShortCodes</key> +			<array> +				<string>:snake:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Reptile</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐉</string> +			<key>ShortCodes</key> +			<array> +				<string>:dragon:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Reptile</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦖</string> +			<key>ShortCodes</key> +			<array> +				<string>:t-rex:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Reptile</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐳</string> +			<key>ShortCodes</key> +			<array> +				<string>:whale:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Marine</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐬</string> +			<key>ShortCodes</key> +			<array> +				<string>:dolphin:</string> +				<string>:flipper:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Marine</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐟</string> +			<key>ShortCodes</key> +			<array> +				<string>:fish:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Marine</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐡</string> +			<key>ShortCodes</key> +			<array> +				<string>:blowfish:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Marine</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐙</string> +			<key>ShortCodes</key> +			<array> +				<string>:octopus:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Marine</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐋</string> +			<key>ShortCodes</key> +			<array> +				<string>:whale2:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Marine</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦭</string> +			<key>ShortCodes</key> +			<array> +				<string>:seal:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Marine</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐠</string> +			<key>ShortCodes</key> +			<array> +				<string>:tropical_fish:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Marine</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦈</string> +			<key>ShortCodes</key> +			<array> +				<string>:shark:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Marine</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐚</string> +			<key>ShortCodes</key> +			<array> +				<string>:shell:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Marine</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐌</string> +			<key>ShortCodes</key> +			<array> +				<string>:snail:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bug</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐛</string> +			<key>ShortCodes</key> +			<array> +				<string>:bug:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bug</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐝</string> +			<key>ShortCodes</key> +			<array> +				<string>:bee:</string> +				<string>:honeybee:</string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bug</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐞</string> +			<key>ShortCodes</key> +			<array> +				<string>:lady_beetle:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bug</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🪳</string> +			<key>ShortCodes</key> +			<array> +				<string>:cockroach:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bug</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🕸️</string> +			<key>ShortCodes</key> +			<array> +				<string>:spider_web:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bug</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦟</string> +			<key>ShortCodes</key> +			<array> +				<string>:mosquito:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bug</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🪱</string> +			<key>ShortCodes</key> +			<array> +				<string>:worm:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bug</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦋</string> +			<key>ShortCodes</key> +			<array> +				<string>:butterfly:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bug</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🐜</string> +			<key>ShortCodes</key> +			<array> +				<string>:ant:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bug</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🪲</string> +			<key>ShortCodes</key> +			<array> +				<string>:beetle:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bug</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦗</string> +			<key>ShortCodes</key> +			<array> +				<string>:cricket:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bug</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🕷️</string> +			<key>ShortCodes</key> +			<array> +				<string>:spider:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bug</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦂</string> +			<key>ShortCodes</key> +			<array> +				<string>:scorpion:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bug</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🪰</string> +			<key>ShortCodes</key> +			<array> +				<string>:fly:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bug</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🦠</string> +			<key>ShortCodes</key> +			<array> +				<string>:microbe:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Animal Bug</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💐</string> +			<key>ShortCodes</key> +			<array> +				<string>:bouquet:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Flower</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>💮</string> +			<key>ShortCodes</key> +			<array> +				<string>:white_flower:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Flower</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌹</string> +			<key>ShortCodes</key> +			<array> +				<string>:rose:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Flower</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌺</string> +			<key>ShortCodes</key> +			<array> +				<string>:hibiscus:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Flower</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌼</string> +			<key>ShortCodes</key> +			<array> +				<string>:blossom:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Flower</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌸</string> +			<key>ShortCodes</key> +			<array> +				<string>:cherry_blossom:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Flower</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🏵️</string> +			<key>ShortCodes</key> +			<array> +				<string>:rosette:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Flower</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥀</string> +			<key>ShortCodes</key> +			<array> +				<string>:wilted_flower:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Flower</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌻</string> +			<key>ShortCodes</key> +			<array> +				<string>:sunflower:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Flower</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌷</string> +			<key>ShortCodes</key> +			<array> +				<string>:tulip:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Flower</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌱</string> +			<key>ShortCodes</key> +			<array> +				<string>:seedling:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Other</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌲</string> +			<key>ShortCodes</key> +			<array> +				<string>:evergreen_tree:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Other</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌴</string> +			<key>ShortCodes</key> +			<array> +				<string>:palm_tree:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Other</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌾</string> +			<key>ShortCodes</key> +			<array> +				<string>:ear_of_rice:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Other</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>☘️</string> +			<key>ShortCodes</key> +			<array> +				<string>:shamrock:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Other</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍁</string> +			<key>ShortCodes</key> +			<array> +				<string>:maple_leaf:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Other</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍃</string> +			<key>ShortCodes</key> +			<array> +				<string>:leaves:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Other</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🪴</string> +			<key>ShortCodes</key> +			<array> +				<string>:potted_plant:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Other</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌳</string> +			<key>ShortCodes</key> +			<array> +				<string>:deciduous_tree:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Other</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌵</string> +			<key>ShortCodes</key> +			<array> +				<string>:cactus:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Other</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌿</string> +			<key>ShortCodes</key> +			<array> +				<string>:herb:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Other</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍀</string> +			<key>ShortCodes</key> +			<array> +				<string>:four_leaf_clover:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Other</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍂</string> +			<key>ShortCodes</key> +			<array> +				<string>:fallen_leaf:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Plant Other</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍇</string> +			<key>ShortCodes</key> +			<array> +				<string>:grapes:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍉</string> +			<key>ShortCodes</key> +			<array> +				<string>:watermelon:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍋</string> +			<key>ShortCodes</key> +			<array> +				<string>:lemon:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍍</string> +			<key>ShortCodes</key> +			<array> +				<string>:pineapple:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍎</string> +			<key>ShortCodes</key> +			<array> +				<string>:apple:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍐</string> +			<key>ShortCodes</key> +			<array> +				<string>:pear:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍒</string> +			<key>ShortCodes</key> +			<array> +				<string>:cherries:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🫐</string> +			<key>ShortCodes</key> +			<array> +				<string>:blueberries:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍅</string> +			<key>ShortCodes</key> +			<array> +				<string>:tomato:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥥</string> +			<key>ShortCodes</key> +			<array> +				<string>:coconut:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍈</string> +			<key>ShortCodes</key> +			<array> +				<string>:melon:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍊</string> +			<key>ShortCodes</key> +			<array> +				<string>:mandarin:</string> +				<string>:orange:</string> +				<string>:tangerine:</string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍌</string> +			<key>ShortCodes</key> +			<array> +				<string>:banana:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥭</string> +			<key>ShortCodes</key> +			<array> +				<string>:mango:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍏</string> +			<key>ShortCodes</key> +			<array> +				<string>:green_apple:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍑</string> +			<key>ShortCodes</key> +			<array> +				<string>:peach:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍓</string> +			<key>ShortCodes</key> +			<array> +				<string>:strawberry:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥝</string> +			<key>ShortCodes</key> +			<array> +				<string>:kiwi_fruit:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🫒</string> +			<key>ShortCodes</key> +			<array> +				<string>:olive:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Fruit</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥑</string> +			<key>ShortCodes</key> +			<array> +				<string>:avocado:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Vegetable</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥔</string> +			<key>ShortCodes</key> +			<array> +				<string>:potato:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Vegetable</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌽</string> +			<key>ShortCodes</key> +			<array> +				<string>:corn:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Vegetable</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🫑</string> +			<key>ShortCodes</key> +			<array> +				<string>:bell_pepper:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Vegetable</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥬</string> +			<key>ShortCodes</key> +			<array> +				<string>:leafy_green:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Vegetable</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧄</string> +			<key>ShortCodes</key> +			<array> +				<string>:garlic:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Vegetable</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍄</string> +			<key>ShortCodes</key> +			<array> +				<string>:mushroom:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Vegetable</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌰</string> +			<key>ShortCodes</key> +			<array> +				<string>:chestnut:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Vegetable</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍆</string> +			<key>ShortCodes</key> +			<array> +				<string>:eggplant:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Vegetable</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥕</string> +			<key>ShortCodes</key> +			<array> +				<string>:carrot:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Vegetable</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌶️</string> +			<key>ShortCodes</key> +			<array> +				<string>:hot_pepper:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Vegetable</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥒</string> +			<key>ShortCodes</key> +			<array> +				<string>:cucumber:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Vegetable</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥦</string> +			<key>ShortCodes</key> +			<array> +				<string>:broccoli:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Vegetable</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧅</string> +			<key>ShortCodes</key> +			<array> +				<string>:onion:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Vegetable</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥜</string> +			<key>ShortCodes</key> +			<array> +				<string>:peanuts:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Vegetable</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍞</string> +			<key>ShortCodes</key> +			<array> +				<string>:bread:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥖</string> +			<key>ShortCodes</key> +			<array> +				<string>:baguette_bread:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥨</string> +			<key>ShortCodes</key> +			<array> +				<string>:pretzel:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥞</string> +			<key>ShortCodes</key> +			<array> +				<string>:pancakes:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧀</string> +			<key>ShortCodes</key> +			<array> +				<string>:cheese:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍗</string> +			<key>ShortCodes</key> +			<array> +				<string>:poultry_leg:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥓</string> +			<key>ShortCodes</key> +			<array> +				<string>:bacon:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍟</string> +			<key>ShortCodes</key> +			<array> +				<string>:fries:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌭</string> +			<key>ShortCodes</key> +			<array> +				<string>:hotdog:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌮</string> +			<key>ShortCodes</key> +			<array> +				<string>:taco:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🫔</string> +			<key>ShortCodes</key> +			<array> +				<string>:tamale:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧆</string> +			<key>ShortCodes</key> +			<array> +				<string>:falafel:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍳</string> +			<key>ShortCodes</key> +			<array> +				<string>:fried_egg:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍲</string> +			<key>ShortCodes</key> +			<array> +				<string>:stew:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥣</string> +			<key>ShortCodes</key> +			<array> +				<string>:bowl_with_spoon:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍿</string> +			<key>ShortCodes</key> +			<array> +				<string>:popcorn:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧂</string> +			<key>ShortCodes</key> +			<array> +				<string>:salt:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥐</string> +			<key>ShortCodes</key> +			<array> +				<string>:croissant:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🫓</string> +			<key>ShortCodes</key> +			<array> +				<string>:flatbread:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥯</string> +			<key>ShortCodes</key> +			<array> +				<string>:bagel:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧇</string> +			<key>ShortCodes</key> +			<array> +				<string>:waffle:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍖</string> +			<key>ShortCodes</key> +			<array> +				<string>:meat_on_bone:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥩</string> +			<key>ShortCodes</key> +			<array> +				<string>:cut_of_meat:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍔</string> +			<key>ShortCodes</key> +			<array> +				<string>:hamburger:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍕</string> +			<key>ShortCodes</key> +			<array> +				<string>:pizza:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥪</string> +			<key>ShortCodes</key> +			<array> +				<string>:sandwich:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🌯</string> +			<key>ShortCodes</key> +			<array> +				<string>:burrito:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥙</string> +			<key>ShortCodes</key> +			<array> +				<string>:stuffed_flatbread:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥚</string> +			<key>ShortCodes</key> +			<array> +				<string>:egg:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥘</string> +			<key>ShortCodes</key> +			<array> +				<string>:shallow_pan_of_food:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🫕</string> +			<key>ShortCodes</key> +			<array> +				<string>:fondue:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥗</string> +			<key>ShortCodes</key> +			<array> +				<string>:green_salad:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🧈</string> +			<key>ShortCodes</key> +			<array> +				<string>:butter:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥫</string> +			<key>ShortCodes</key> +			<array> +				<string>:canned_food:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Prepared</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍱</string> +			<key>ShortCodes</key> +			<array> +				<string>:bento:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Asian</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍙</string> +			<key>ShortCodes</key> +			<array> +				<string>:rice_ball:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Asian</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍛</string> +			<key>ShortCodes</key> +			<array> +				<string>:curry:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Asian</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍝</string> +			<key>ShortCodes</key> +			<array> +				<string>:spaghetti:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Asian</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍢</string> +			<key>ShortCodes</key> +			<array> +				<string>:oden:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Asian</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍤</string> +			<key>ShortCodes</key> +			<array> +				<string>:fried_shrimp:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Asian</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥮</string> +			<key>ShortCodes</key> +			<array> +				<string>:moon_cake:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Asian</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥟</string> +			<key>ShortCodes</key> +			<array> +				<string>:dumpling:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Asian</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥡</string> +			<key>ShortCodes</key> +			<array> +				<string>:takeout_box:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Asian</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍘</string> +			<key>ShortCodes</key> +			<array> +				<string>:rice_cracker:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Asian</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍚</string> +			<key>ShortCodes</key> +			<array> +				<string>:rice:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Asian</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍜</string> +			<key>ShortCodes</key> +			<array> +				<string>:ramen:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Asian</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍠</string> +			<key>ShortCodes</key> +			<array> +				<string>:sweet_potato:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Asian</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍣</string> +			<key>ShortCodes</key> +			<array> +				<string>:sushi:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Asian</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍥</string> +			<key>ShortCodes</key> +			<array> +				<string>:fish_cake:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Asian</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🍡</string> +			<key>ShortCodes</key> +			<array> +				<string>:dango:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Asian</string> +				<string></string> +			</array> +		</map> +		<map> +			<key>Character</key> +			<string>🥠</string> +			<key>ShortCodes</key> +			<array> +				<string>:fortune_cookie:</string> +				<string></string> +				<string></string> +			</array> +			<key>Categories</key> +			<array> +				<string>Food Asian</string> +				<string></string> +			</array> +		</map> +	</array> +</llsd> diff --git a/indra/newview/skins/default/xui/en/floater_emoji_complete.xml b/indra/newview/skins/default/xui/en/floater_emoji_complete.xml new file mode 100644 index 0000000000..e9ea8f4de7 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_emoji_complete.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + can_close="false" + can_dock="false" + can_drag_on_left="false" + can_minimize="false" + can_resize="false" + can_tear_off="false" + header_height="0" + layout="topleft" + legacy_header_height="0" + height="40" + single_instance="true" + width="240" + > +	<emoji_complete +	 autosize="true" +	 height="30" +	 follows="top|left" +	 layout="topleft" +	 left="5" +	 max_emoji="7" +	 name="emoji_complete_ctrl" +	 top="5" +	 width="230" +	 > +	</emoji_complete> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_preview_notecard.xml b/indra/newview/skins/default/xui/en/floater_preview_notecard.xml index dcbdfa8794..ac5467c036 100644 --- a/indra/newview/skins/default/xui/en/floater_preview_notecard.xml +++ b/indra/newview/skins/default/xui/en/floater_preview_notecard.xml @@ -73,6 +73,8 @@       spellcheck="true"       tab_group="1"       top="46" +     use_color="true" +     show_emoji_helper="true"       width="392"       word_wrap="true">          Loading... diff --git a/indra/newview/skins/default/xui/en/fonts.xml b/indra/newview/skins/default/xui/en/fonts.xml index d88c267a95..40045625fd 100644 --- a/indra/newview/skins/default/xui/en/fonts.xml +++ b/indra/newview/skins/default/xui/en/fonts.xml @@ -3,6 +3,7 @@    <font name="default" comment="default font files (global fallbacks)">      <file>DejaVuSans.ttf</file> +    <file functor="is_emoji">TwemojiSVG.ttf</file>      <os name="Windows">        <file>meiryo.TTC</file>        <file>MSGOTHIC.TTC</file> @@ -69,6 +70,11 @@      <file>DejaVuSans-BoldOblique.ttf</file>    </font> +  <font name="Emoji" +	comment="Name of emoji font"> +    <file>TwemojiSVG.ttf</file> +  </font> +    <font name="Monospace"  	comment="Name of monospace font">      <file>DejaVuSansMono.ttf</file> diff --git a/indra/newview/skins/default/xui/en/menu_login.xml b/indra/newview/skins/default/xui/en/menu_login.xml index 96fac1c6e8..40399b33ef 100644 --- a/indra/newview/skins/default/xui/en/menu_login.xml +++ b/indra/newview/skins/default/xui/en/menu_login.xml @@ -161,6 +161,32 @@        <menu_item_separator />        <menu         create_jump_keys="true" +       label="Fonts" +       name="Fonts" +       tear_off="true"> +        <menu_item_call +         label="Show Font Test" +         name="Show Font Test"> +          <menu_item_call.on_click +           function="Floater.Show" +           parameter="font_test" /> +        </menu_item_call> +        <menu_item_separator /> +        <menu_item_call +         label="Dump Fonts" +         name="Dump Fonts"> +          <menu_item_call.on_click +           function="Develop.Fonts.Dump" /> +        </menu_item_call> +        <menu_item_call +         label="Dump Font Textures" +         name="Dump Font Textures"> +          <menu_item_call.on_click +           function="Develop.Fonts.DumpTextures" /> +        </menu_item_call> +      </menu> +      <menu +       create_jump_keys="true"         label="UI Tests"         name="UI Tests"         tear_off="true"> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 58584345a9..66b70512a6 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -3484,6 +3484,18 @@ function="World.EnvPreset"               parameter="https://cryptic-ridge-1632.herokuapp.com/"/>            </menu_item_call>            <menu_item_call +           label="Dump Fonts" +           name="Dump Fonts"> +            <menu_item_call.on_click +             function="Develop.Fonts.Dump" /> +          </menu_item_call> +          <menu_item_call +           label="Dump Font Textures" +           name="Dump Font Textures"> +            <menu_item_call.on_click +             function="Develop.Fonts.DumpTextures" /> +          </menu_item_call> +          <menu_item_call               label="Dump SelectMgr"               name="Dump SelectMgr">                  <menu_item_call.on_click diff --git a/indra/newview/skins/default/xui/en/widgets/chat_editor.xml b/indra/newview/skins/default/xui/en/widgets/chat_editor.xml index f9facb593a..c550f634e5 100644 --- a/indra/newview/skins/default/xui/en/widgets/chat_editor.xml +++ b/indra/newview/skins/default/xui/en/widgets/chat_editor.xml @@ -1,4 +1,7 @@  <?xml version="1.0" encoding="utf-8" standalone="yes" ?>  <chat_editor    name="chat_editor" -  show_context_menu="true"/> +  show_context_menu="true" +  show_emoji_helper="true" +  use_color="true" +  /> diff --git a/indra/newview/skins/default/xui/en/widgets/emoji_complete.xml b/indra/newview/skins/default/xui/en/widgets/emoji_complete.xml new file mode 100644 index 0000000000..370f1d174e --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/emoji_complete.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<emoji_complete +  autosize="false" +  font="EmojiHuge" +  hover_image="ListItem_Over" +  max_emoji="7" +  padding="8" +  selected_image="ListItem_Select" +  > +</emoji_complete> diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 59eb18b734..8cc1cc5bb4 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -141,7 +141,7 @@ class ViewerManifest(LLManifest):                  self.path("*.tga")              # Include our fonts -            with self.prefix(src_dst="fonts"): +            with self.prefix(src="../packages/fonts",src_dst="fonts"):                  self.path("*.ttf")                  self.path("*.txt")  | 
