diff --git a/.gitignore b/.gitignore
index 4af34870cf621d95cb8aa4d1991d8fe993da8e19..d664c0b1d417bb7de180816caccc4ff7776fa210 100755
--- a/.gitignore
+++ b/.gitignore
@@ -46,6 +46,7 @@ indra/newview/dbghelp.dll
 indra/newview/filters.xml
 indra/newview/fmod.dll
 indra/newview/fmod.log
+indra/newview/fonts
 indra/newview/mozilla-theme
 indra/newview/mozilla-universal-darwin.tgz
 indra/newview/pilot.txt
diff --git a/autobuild.xml b/autobuild.xml
index 5424e0629a12ca0e1e392e11edaf4fadbde42f0c..ac7459ddca4b458256da061d1195ea7ae45f208e 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -807,6 +807,36 @@
         <key>version</key>
         <string>2.01.07.555883</string>
       </map>
+      <key>fonts</key>
+      <map>
+        <key>copyright</key>
+        <string>Copyright various</string>
+        <key>description</key>
+        <string>Viewer fonts</string>
+        <key>license</key>
+        <string>Various open source</string>
+        <key>license_file</key>
+        <string>LICENSES/fonts.txt</string>
+        <key>name</key>
+        <string>fonts</string>
+        <key>platforms</key>
+        <map>
+          <key>common</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>65b89f06d9b698b376e1736216dd5623</string>
+              <key>url</key>
+              <string>viewer_fonts-1.0-1.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>common</string>
+          </map>
+        </map>
+        <key>version</key>
+        <string>1.0</string>
+      </map>
       <key>fontconfig</key>
       <map>
         <key>copyright</key>
@@ -916,9 +946,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>7ee200d6b5fa282c7f973ade5615aa86</string>
+              <string>d8f5b352176d5c582d91ce936a700b2c</string>
               <key>url</key>
-              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78594/744011/freetype-2.4.4.557047-windows-557047.tar.bz2</string>
+              <string>freetype-2.10.1.0-windows-72.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows</string>
@@ -928,9 +958,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>69307aaba16ac71531c9c4d930ace993</string>
+              <string>aa8be83cdfcd5cda0343ea72e7d662ca</string>
               <key>url</key>
-              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78593/744010/freetype-2.4.4.557047-windows64-557047.tar.bz2</string>
+              <string>freetype-2.10.1.0-windows64-72.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows64</string>
@@ -1401,6 +1431,96 @@
         <key>version</key>
         <string>2012.1-2</string>
       </map>
+      <key>icu4c</key>
+      <map>
+        <key>copyright</key>
+        <string>Copyright (c) 1995-2011 International Business Machines Corporation and others &lt;http://source.icu-project.org&gt;</string>
+        <key>description</key>
+        <string>ICU is a mature, widely used set of C/C++ and Java libraries providing Unicode and Globalization support for software applications. ICU is widely portable and gives applications the same results on all platforms and between C/C++ and Java software.</string>
+        <key>license</key>
+        <string>ICU, permissive non-copyleft free software license</string>
+        <key>license_file</key>
+        <string>LICENSES/icu.txt</string>
+        <key>name</key>
+        <string>icu4c</string>
+        <key>platforms</key>
+        <map>
+          <key>darwin</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>910edfc0b936389bc8804c42d1008aa8</string>
+              <key>url</key>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/icu4c_3p-update-icu4c/rev/295486/arch/Darwin/installer/icu4c-4.8.1-darwin-295486.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>windows64</string>
+          </map>
+          <key>darwin64</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>2c34cb4e62d1f155d5dad798e69d03c8</string>
+              <key>url</key>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/466/1005/icu4c-4.8.1-darwin64-500388.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>windows64</string>
+          </map>
+          <key>linux</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>0a4423cfd26409f33ae81fb9806a9972</string>
+              <key>url</key>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-icu4c/rev/314152/arch/Linux/installer/icu4c-4.8.1-linux-314152.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>windows64</string>
+          </map>
+          <key>linux64</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>b327031733c36efe2eee4582aefb2d66</string>
+              <key>url</key>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/462/995/icu4c-4.8.1-linux64-500388.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>windows64</string>
+          </map>
+          <key>windows</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+          <string>af354f6ab6d2cecf266c79031977e3e0</string>
+          <key>url</key>
+          <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/492/1078/icu4c-4.8.1-windows-500388.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>windows64</string>
+          </map>
+          <key>windows64</key>
+          <map>
+            <key>archive</key>
+            <map>
+              <key>hash</key>
+              <string>e8b20cea748b90ea18bb63dfba06e059</string>
+              <key>url</key>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/491/1075/icu4c-4.8.1-windows64-500388.tar.bz2</string>
+            </map>
+            <key>name</key>
+            <string>windows64</string>
+          </map>
+        </map>
+        <key>version</key>
+        <string>4.8.1</string>
+      </map>
       <key>jpeglib</key>
       <map>
         <key>copyright</key>
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt
index cca305c741e1f968eb9d064e3148996fede733a3..c65906b853b08dc25de62516e7149e4072716eb3 100644
--- a/indra/cmake/CMakeLists.txt
+++ b/indra/cmake/CMakeLists.txt
@@ -29,6 +29,7 @@ set(cmake_SOURCE_FILES
     FindBerkeleyDB.cmake
     FindGLH.cmake
     FindHUNSPELL.cmake
+    FindICU4C.cmake
     FindJsonCpp.cmake
     FindNDOF.cmake
     FindOpenJPEG.cmake
@@ -45,6 +46,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 0000000000000000000000000000000000000000..327d761a88a3226e7d1849110ce02e9661bf2ae3
--- /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 0000000000000000000000000000000000000000..007a9b6937a43262d6a64bee22015002766a0bcb
--- /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 fc5bdedb5afb3a5a3a23b3146c9500e582fe4119..f213de100c9a2ffa86844f21aeca90cb8c029ca8 100644
--- a/indra/cmake/ViewerMiscLibs.cmake
+++ b/indra/cmake/ViewerMiscLibs.cmake
@@ -10,3 +10,4 @@ if (NOT USESYSTEMLIBS)
   use_prebuilt_binary(slvoice)
 endif(NOT USESYSTEMLIBS)
 
+use_prebuilt_binary(fonts)
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 22dfe12e40ea00c314ca7de4d4c068d1e44707a4..89eefd8ce1b20e585778bb78ac612e3f674a7b3f 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)
@@ -289,6 +290,7 @@ target_link_libraries(
     ${APRUTIL_LIBRARIES}
     ${APR_LIBRARIES}
     ${EXPAT_LIBRARIES}
+    ${ICU4C_LIBRARY}
     ${JSONCPP_LIBRARIES}
     ${ZLIB_LIBRARIES}
     ${WINDOWS_LIBRARIES}
diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp
index 0290eea1430b89d088fcdfae45042669b0252ea9..9779e03e8b57376bb74aad20b89f15062d373ba9 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
@@ -872,6 +873,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 4263122f366f8668f5d76b460cab418292c54a4a..32e66a48b75dd4c60c3dc886238f4a68f7d96cc3 100644
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -187,6 +187,8 @@ class LL_COMMON_API LLStringOps
 	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/llfontbitmapcache.cpp b/indra/llrender/llfontbitmapcache.cpp
index f128636ab29b33b142dd1bf6d880c780da67c801..754adb14cbfa902040868bb9b49ff51c698542bc 100644
--- a/indra/llrender/llfontbitmapcache.cpp
+++ b/indra/llrender/llfontbitmapcache.cpp
@@ -30,152 +30,165 @@
 #include "llfontbitmapcache.h"
 
 LLFontBitmapCache::LLFontBitmapCache()
-:	LLTrace::MemTrackable<LLFontBitmapCache>("LLFontBitmapCache"),
-	mNumComponents(0),
-	mBitmapWidth(0),
-	mBitmapHeight(0),
-	mBitmapNum(-1),
-	mMaxCharWidth(0),
-	mMaxCharHeight(0),
-	mCurrentOffsetX(1),
-	mCurrentOffsetY(1)
+	: LLTrace::MemTrackable<LLFontBitmapCache>("LLFontBitmapCache")
+	, mBitmapWidth(0)
+	, mBitmapHeight(0)
+	, mMaxCharWidth(0)
+	, mMaxCharHeight(0)
 {
+	// *TODO: simplify with initializer after VS2017
+	for (U32 idx = 0, cnt = static_cast<U32>(EFontGlyphType::Count); idx < cnt; idx++)
+	{
+		mCurrentOffsetX[idx] = 1;
+		mCurrentOffsetY[idx] = 1;
+	}
 }
 
 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)
 	{
-		if ((mBitmapNum<0) || (mCurrentOffsetY + 2*mMaxCharHeight + 2) > mBitmapHeight)
+		return FALSE;
+	}
+
+	const U32 bitmap_idx = static_cast<U32>(bitmap_type);
+	if (mImageRawVec[bitmap_idx].empty() || (mCurrentOffsetX[bitmap_idx] + width + 1) > mBitmapWidth)
+	{
+		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);
-
-			claimMem(image_raw);
-			claimMem(image_gl);
 		}
 		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 (std::vector<LLPointer<LLImageGL> >::iterator it = mImageGLVec[idx].begin(); it != mImageGLVec[idx].end(); ++it)
+		{
+			(*it)->destroyGLTexture();
+		}
 	}
 }
 
 void LLFontBitmapCache::reset()
 {
-	for (std::vector<LLPointer<LLImageRaw> >::iterator it = mImageRawVec.begin(), end_it = mImageRawVec.end();
-		it != end_it;
-		++it)
+	for (U32 idx = 0, cnt = static_cast<U32>(EFontGlyphType::Count); idx < cnt; idx++)
 	{
-		disclaimMem(**it);
+		for (std::vector<LLPointer<LLImageRaw> >::iterator it = mImageRawVec[idx].begin(), end_it = mImageRawVec[idx].end(); it != end_it; ++it)
+		{
+			disclaimMem(**it);
+		}
+		mImageRawVec[idx].clear();
 	}
-	mImageRawVec.clear();
 
-	for (std::vector<LLPointer<LLImageGL> >::iterator it = mImageGLVec.begin(), end_it = mImageGLVec.end();
-		it != end_it;
-		++it)
+	for (U32 idx = 0, cnt = static_cast<U32>(EFontGlyphType::Count); idx < cnt; idx++)
 	{
-		disclaimMem(**it);
+		for (std::vector<LLPointer<LLImageGL> >::iterator it = mImageGLVec[idx].begin(), end_it = mImageGLVec[idx].end(); it != end_it; ++it)
+		{
+			disclaimMem(**it);
+		}
+		mImageGLVec[idx].clear();
+		mCurrentOffsetX[idx] = 1;
+		mCurrentOffsetY[idx] = 1;
 	}
-	mImageGLVec.clear();
 	
 	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 75df3a94a796fab48d4f7c63c3f03c95beef44c5..5d0094fd6978f09d388699ae9379baf1e2dfc55a 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 : public LLTrace::MemTrackable<LLFontBitmapCache>
@@ -39,35 +47,35 @@ class LLFontBitmapCache : public LLTrace::MemTrackable<LLFontBitmapCache>
 	~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 mCurrentOffsetX[static_cast<U32>(EFontGlyphType::Count)];
+	S32 mCurrentOffsetY[static_cast<U32>(EFontGlyphType::Count)];
 	S32 mMaxCharWidth;
 	S32 mMaxCharHeight;
-	S32 mCurrentOffsetX;
-	S32 mCurrentOffsetY;
-	std::vector<LLPointer<LLImageRaw> >	mImageRawVec;
-	std::vector<LLPointer<LLImageGL> > mImageGLVec;
+	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 c41730ebaaacea9d369885fd04380f3821224f93..25740a5f87e6e3849f8df70a1565c51883f79b9f 100644
--- a/indra/llrender/llfontfreetype.cpp
+++ b/indra/llrender/llfontfreetype.cpp
@@ -40,8 +40,10 @@
 #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"
@@ -89,8 +91,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 +102,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()
 :	LLTrace::MemTrackable<LLFontFreetype>("LLFontFreetype"),
 	mFontBitmapCachep(new LLFontBitmapCache),
@@ -157,7 +175,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.
@@ -221,7 +239,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);
 	claimMem(mFontBitmapCachep);
 
 
@@ -234,7 +252,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;
@@ -327,14 +345,11 @@ void LLFontFreetype::clearFontStreams()
 }
 #endif
 
-void LLFontFreetype::setFallbackFonts(const font_vector_t &font)
+void LLFontFreetype::addFallbackFont(const LLPointer<LLFontFreetype>& fallback_font, const char_functor_t& functor)
 {
-	mFallbackFonts = font;
-}
-
-const LLFontFreetype::font_vector_t &LLFontFreetype::getFallbackFonts() const
-{
-	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
@@ -358,7 +373,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;
@@ -390,10 +405,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;
@@ -424,59 +439,90 @@ 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
 {
 	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;
@@ -487,8 +533,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)
@@ -523,62 +573,63 @@ 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;
@@ -586,16 +637,23 @@ void LLFontFreetype::insertGlyphInfo(llwchar wch, LLFontGlyphInfo* gi) const
 	else
 	{
 		claimMem(gi);
-		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) );
 
@@ -605,7 +663,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.
@@ -615,11 +673,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);
 			}
 		}
 	}
@@ -643,7 +699,7 @@ void LLFontFreetype::resetBitmapCache()
 	if(!mIsFallback)
 	{
 		// Add the empty glyph
-		addGlyphFromFont(this, 0, 0);
+		addGlyphFromFont(this, 0, 0, EFontGlyphType::Grayscale);
 	}
 }
 
@@ -657,6 +713,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;
@@ -672,9 +756,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 1afe84e770f6c289708cc6bbff9d007971cf9da1..8afc5e3ed54384cbf60c6c42d5f97ba854d5f5fe 100644
--- a/indra/llrender/llfontfreetype.h
+++ b/indra/llrender/llfontfreetype.h
@@ -56,9 +56,11 @@ class LLFontManager
 
 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 @@ class LLFontFreetype : public LLRefCount, public LLTrace::MemTrackable<LLFontFre
 
 	// 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 @@ class LLFontFreetype : public LLRefCount, public LLTrace::MemTrackable<LLFontFre
 	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 @@ class LLFontFreetype : public LLRefCount, public LLTrace::MemTrackable<LLFontFre
 	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 @@ class LLFontFreetype : public LLRefCount, public LLTrace::MemTrackable<LLFontFre
 
 	const std::string& getName() const;
 
+	void       dumpFontBitmaps() const;
 	const LLFontBitmapCache* getFontBitmapCache() const;
 
 	void setStyle(U8 style);
@@ -151,10 +152,11 @@ class LLFontFreetype : public LLRefCount, public LLTrace::MemTrackable<LLFontFre
 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 @@ class LLFontFreetype : public LLRefCount, public LLTrace::MemTrackable<LLFontFre
 #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/llfontgl.cpp b/indra/llrender/llfontgl.cpp
index 86a4c35e6dec1ef309c370bdc2b0a40fb7dff272..4770f7939542088483e06efdf089de52b11c8883 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)
@@ -112,14 +112,14 @@ S32 LLFontGL::getNumFaces(const std::string& filename)
 static LLTrace::BlockTimerStatHandle FTM_RENDER_FONTS("Fonts");
 
 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;
@@ -140,12 +140,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_RECORD_BLOCK_TIME(FTM_RENDER_FONTS);
 
@@ -280,7 +280,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++)
 	{
@@ -290,7 +290,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)
 		{
@@ -298,8 +298,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.
@@ -313,8 +313,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);
 		}
 	
@@ -347,7 +347,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;
@@ -357,7 +357,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);
 		}
 
@@ -411,7 +411,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();
 	}
 
@@ -425,19 +426,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
@@ -514,7 +515,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);
@@ -534,7 +535,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.
@@ -616,7 +617,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)
 			{
@@ -641,7 +642,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);
 		}
 
@@ -688,7 +689,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
@@ -763,7 +764,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);
@@ -793,7 +794,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);
 		}
 
@@ -831,6 +832,26 @@ void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::st
 	}
 }
 
+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
diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h
index 10891faed9c1318b918b1beb6bb6d2c64f741c8f..a60feb87cbd7ee616e8c98533fd28622299685f1 100644
--- a/indra/llrender/llfontgl.h
+++ b/indra/llrender/llfontgl.h
@@ -87,7 +87,7 @@ class LLFontGL
 
 	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 @@ class LLFontGL
 				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 @@ class LLFontGL
 				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 @@ class LLFontGL
 				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;
 
@@ -163,6 +166,10 @@ class LLFontGL
 
 	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();
diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp
index 33a33af160d2e086061ac4ac1f54cac25b1af404..f2dc5771e988ab2123a63c0e23edbc4ad9061c2b 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);
+#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;
@@ -715,11 +731,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 e30c81c63018593ed561b6e9b51f40d9c31e9af8..b0ef72c5ded4ca4112f71f655b78b7506ae2624d 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 @@ class LLFontDescriptor
 	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 @@ class LLFontRegistry
 	bool nameToSize(const std::string& size_name, F32& size);
 
 	void dump();
+	void dumpTextures();
 	
 	const string_vec_t& getUltimateFallbackList() const;
 
diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp
index 285bf9f48456e3107e62661fa093ecf4f5142f2b..835d3b781dc49c7379e9aaf1a465b262848b9f2b 100644
--- a/indra/llui/llfolderviewitem.cpp
+++ b/indra/llui/llfolderviewitem.cpp
@@ -875,7 +875,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()
@@ -954,7 +954,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 );
 	}
 
 	//--------------------------------------------------------------------------------//
@@ -967,7 +967,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 20bea7fe243df33586a94f872862111a0b7dd09d..9005d70b2e13c062181e62e2b020a0a11a689df6 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -161,9 +161,11 @@ 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),
 	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)
@@ -206,6 +208,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 : p.font_valign),
 	mLineSpacingMult(p.line_spacing.multiple),
 	mLineSpacingPixels(p.line_spacing.pixels),
 	mClip(p.clip),
@@ -219,6 +222,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),
@@ -515,7 +519,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);
@@ -1920,8 +1924,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(); }
 
@@ -1945,8 +1947,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(); }
 
@@ -3239,12 +3239,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;
 	
@@ -3258,12 +3259,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 )
@@ -3275,12 +3277,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;
 }
diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h
index 4e966b7cefbb73213bf6527a95bd00fc232f0913..590e7c9dbb5a269c086486b8cb47967ce7e34b64 100644
--- a/indra/llui/lltextbase.h
+++ b/indra/llui/lltextbase.h
@@ -316,6 +316,7 @@ class LLTextBase
 								plain_text,
 								wrap,
 								use_ellipses,
+								use_color,
 								parse_urls,
 								force_urls_external,
 								parse_highlights,
@@ -334,6 +335,8 @@ class LLTextBase
 
 		Optional<LLFontGL::ShadowType>	font_shadow;
 
+		Optional<LLFontGL::VAlign> text_valign;
+
 		Params();
 	};
 
@@ -391,6 +394,7 @@ class LLTextBase
 	// 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;}
@@ -680,8 +684,9 @@ class LLTextBase
 	// 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;
@@ -690,6 +695,7 @@ class LLTextBase
 	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/llview.cpp b/indra/llui/llview.cpp
index b942be2a4a833def1e7ac628d19cc902c0b5315d..2781c991a7198d2e9242d0d72b57bc799bedb1e5 100644
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -1325,7 +1325,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 68b5969ff14fa1900d62a4123f636868ba0717e2..8d902ce618fc0f7d2e6b981332f7bb729e84c1b8 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -18,6 +18,7 @@ include(EXPAT)
 include(FMODSTUDIO)
 include(GLOD)
 include(Hunspell)
+include(ICU4C)
 include(JsonCpp)
 include(LLAppearance)
 include(LLAudio)
@@ -1497,6 +1498,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")
@@ -2042,6 +2049,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 254e2cc42a6d0135cccf2047768b9e44f25e666f..0000000000000000000000000000000000000000
--- 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
deleted file mode 100644
index ec1a2ebaf247e5d2a547a5ec406f6831dabe26bd..0000000000000000000000000000000000000000
Binary files a/indra/newview/fonts/DejaVuSans-Bold.ttf and /dev/null differ
diff --git a/indra/newview/fonts/DejaVuSans-BoldOblique.ttf b/indra/newview/fonts/DejaVuSans-BoldOblique.ttf
deleted file mode 100644
index 1a5576460d5eedbf5e2bcbfacfadb01316a3628f..0000000000000000000000000000000000000000
Binary files a/indra/newview/fonts/DejaVuSans-BoldOblique.ttf and /dev/null differ
diff --git a/indra/newview/fonts/DejaVuSans-Oblique.ttf b/indra/newview/fonts/DejaVuSans-Oblique.ttf
deleted file mode 100644
index becc54992732780a54a3843eea37884768d36c97..0000000000000000000000000000000000000000
Binary files a/indra/newview/fonts/DejaVuSans-Oblique.ttf and /dev/null differ
diff --git a/indra/newview/fonts/DejaVuSans.ttf b/indra/newview/fonts/DejaVuSans.ttf
deleted file mode 100644
index c1b19d870590a7bbf0d238e0cefdce6689d3f342..0000000000000000000000000000000000000000
Binary files a/indra/newview/fonts/DejaVuSans.ttf and /dev/null differ
diff --git a/indra/newview/fonts/DejaVuSansMono.ttf b/indra/newview/fonts/DejaVuSansMono.ttf
deleted file mode 100644
index 6bc854ddae97bf5ab2c4c19058306a2d8f96fd49..0000000000000000000000000000000000000000
Binary files a/indra/newview/fonts/DejaVuSansMono.ttf and /dev/null differ
diff --git a/indra/newview/llexpandabletextbox.cpp b/indra/newview/llexpandabletextbox.cpp
index 3395777aab42424db453fa9314140951e6814f46..2d332f75f52cf6a85fc638315c0da312f7bfb963 100644
--- a/indra/newview/llexpandabletextbox.cpp
+++ b/indra/newview/llexpandabletextbox.cpp
@@ -88,7 +88,7 @@ class LLExpanderSegment : public LLTextSegment
 									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 e67c79a3a0f54f33a27d767c8d78cff4ec5e5a5c..9d16faf0b53858c754357fd95b061eb1df8c37bd 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/lltextureview.cpp b/indra/newview/lltextureview.cpp
index 7a0f69fed565cec8f025e24c68f6a45157cb0cba..29221c0bc5c1724b5caa505441b0084614599d63 100644
--- a/indra/newview/lltextureview.cpp
+++ b/indra/newview/lltextureview.cpp
@@ -612,7 +612,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/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index ad81cb07c19a99d68c641770754134fcc3783220..387bd4cf490d3c6857a03b7a6cfa3bdcc55b14fd 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -9302,6 +9302,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/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 1d13a306ef7a5c22aea857cf0c3bb0db44972470..abacd96111ea3bcafeded65a3473addd183b0c2e 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -1007,7 +1007,7 @@ class LLDebugText
 			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 a6df0792233581c1aabe4c1c268298b99a0003c4..70cb4f602a28c57c2f86eac597e1b27dddb05a99 100644
--- a/indra/newview/llworldmapview.cpp
+++ b/indra/newview/llworldmapview.cpp
@@ -469,7 +469,8 @@ void LLWorldMapView::draw()
 					S32_MAX, //max_chars
 					sMapScale, //max_pixels
 					NULL,
-					TRUE); //use ellipses
+					/*use_ellipses*/TRUE,
+					/*use_color*/FALSE);
 			}
 		}
 	}
diff --git a/indra/newview/skins/default/xui/en/fonts.xml b/indra/newview/skins/default/xui/en/fonts.xml
index d88c267a95e63bccedf1e939fe602e9ff304a858..ed3554632263b3e21d69a76369a1fbea292f5b05 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">Twemoji.ttf</file>
     <os name="Windows">
       <file>meiryo.TTC</file>
       <file>MSGOTHIC.TTC</file>
diff --git a/indra/newview/skins/default/xui/en/menu_login.xml b/indra/newview/skins/default/xui/en/menu_login.xml
index 96fac1c6e845dbd8bbde3fc0c654c28f425f6c6a..40399b33ef2337f263625941ae3c87cd0a9f0972 100644
--- a/indra/newview/skins/default/xui/en/menu_login.xml
+++ b/indra/newview/skins/default/xui/en/menu_login.xml
@@ -159,6 +159,32 @@
              parameter="ui_preview" />
         </menu_item_call>
       <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"
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 72cce2208f9a7310efa58ffdbda1926db13885a2..14846af4ade72dc9c9808874a2c9f57ed22e0cfe 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -3448,6 +3448,18 @@ function="World.EnvPreset"
              function="Advanced.WebContentTest"
              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">
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index b932f431412900740b46d6be13ba26918f2a93f6..281777e49268f47eb4a2b6f38874ade812e3723d 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -140,7 +140,7 @@ def construct(self):
                 self.path("*.tga")
 
             # Include our fonts
-            with self.prefix(src_dst="fonts"):
+            with self.prefix(src="../packages/fonts"):
                 self.path("*.ttf")
                 self.path("*.txt")