Blame SOURCES/0001-Make-Noto-Color-Emoji-font-work-on-Linux.patch

64af5b
From 96012f88aac95147ae1fd4834cea5c5bb184d52b Mon Sep 17 00:00:00 2001
64af5b
From: Khaled Hosny <khaledhosny@eglug.org>
64af5b
Date: Tue, 27 Aug 2019 15:19:15 +0200
64af5b
Subject: [PATCH] Make Noto Color Emoji font work on Linux
64af5b
MIME-Version: 1.0
64af5b
Content-Type: text/plain; charset=UTF-8
64af5b
Content-Transfer-Encoding: 8bit
64af5b
64af5b
Noto Color Emoji is a bitmap color font, Cairo knows how to scale such
64af5b
fonts and FontConfig will identify them as scalable but not outline
64af5b
fonts, so change the FontConfig checks to checks for scalability.
64af5b
64af5b
Make sft.cxx:doOpenTTFont() accept non-outline fonts, the text will not
64af5b
show in PDF but that is not worse than the status quo.
64af5b
64af5b
Reviewed-on: https://gerrit.libreoffice.org/78218
64af5b
Tested-by: Jenkins
64af5b
Reviewed-by: Khaled Hosny <khaledhosny@eglug.org>
64af5b
(cherry picked from commit dcf7792da2aa2a1ef774a124f7b21f68fff0fd15)
64af5b
64af5b
Change-Id: I756c718296d2c43e3165cd2f07b11bbb981318d3
64af5b
64af5b
Related: rhbz#1648281 improve fontconfig fallback for emojis
64af5b
64af5b
disregard text language for emoji and tag with und-zsye to
64af5b
get fontconfig to give us the default emoji font
64af5b
64af5b
Change-Id: I8f94b0c41dea3204c9db77b96ad8f0d98bae2239
64af5b
64af5b
ctrl+shift+e emoji ibus engine problems converting UCS-4 positions to UTF-16
64af5b
64af5b
e.g. ctrl+shift+e type rabbit then space in writer and the len of underline
64af5b
is 2 which should encompass the displayed e + 2 UTF-16 units
64af5b
64af5b
Change-Id: I424db7dd6cbcc5845922ac17208fed643e672dbd
64af5b
64af5b
rework IM underline impl wrt mix of UTF-8/16/32 units
64af5b
64af5b
e.g. ctrl+shift+e type boy then space twice in writer. The UTF-32 units
64af5b
are 0x65 0x1f466 0x1f3fb. The underline should encompass the whole range,
64af5b
prior to this the trailing Emoji Modifier Fitzpatrick was separated from
64af5b
the boy base emoji by an incomplete underline
64af5b
64af5b
Reviewed-on: https://gerrit.libreoffice.org/78878
64af5b
Tested-by: Jenkins
64af5b
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
64af5b
Tested-by: Caolán McNamara <caolanm@redhat.com>
64af5b
(cherry picked from commit 5e4d564e27d062a48fd04cb7263b769819dd3a50)
64af5b
64af5b
Change-Id: I2e846e8eeedf96f341ed7f50d504883768e9eff0
64af5b
---
64af5b
 vcl/source/font/fontmetric.cxx                |  4 +-
64af5b
 vcl/source/fontsubset/sft.cxx                 |  5 +-
64af5b
 vcl/unx/generic/fontmanager/fontconfig.cxx    | 60 +++++++++--------
64af5b
 .../generic/glyphs/freetype_glyphcache.cxx    |  6 +-
64af5b
 vcl/unx/gtk3/gtk3gtkframe.cxx                 | 64 +++++++++++++------
64af5b
 5 files changed, 87 insertions(+), 52 deletions(-)
64af5b
64af5b
diff --git a/vcl/source/font/fontmetric.cxx b/vcl/source/font/fontmetric.cxx
64af5b
index cd0b9f8557e9..816525c8773e 100644
64af5b
--- a/vcl/source/font/fontmetric.cxx
64af5b
+++ b/vcl/source/font/fontmetric.cxx
64af5b
@@ -462,8 +462,8 @@ void ImplFontMetricData::ImplCalcLineSpacing(const std::vector<uint8_t>& rHheaDa
64af5b
     if (mnAscent || mnDescent)
64af5b
         mnIntLeading = mnAscent + mnDescent - mnHeight;
64af5b
 
64af5b
-    SAL_INFO("vcl.gdi.fontmetric",
64af5b
-                  "fsSelection: "   << rInfo.fsSelection
64af5b
+    SAL_INFO("vcl.gdi.fontmetric", GetFamilyName()
64af5b
+             << ": fsSelection: "   << rInfo.fsSelection
64af5b
              << ", typoAscender: "  << rInfo.typoAscender
64af5b
              << ", typoDescender: " << rInfo.typoDescender
64af5b
              << ", typoLineGap: "   << rInfo.typoLineGap
64af5b
diff --git a/vcl/source/fontsubset/sft.cxx b/vcl/source/fontsubset/sft.cxx
64af5b
index 365b9401b95e..04921294ab21 100644
64af5b
--- a/vcl/source/fontsubset/sft.cxx
64af5b
+++ b/vcl/source/fontsubset/sft.cxx
64af5b
@@ -1666,7 +1666,10 @@ static int doOpenTTFont( sal_uInt32 facenum, TrueTypeFont* t )
64af5b
         /* TODO: implement to get subsetting */
64af5b
         assert(t->goffsets != nullptr);
64af5b
     } else {
64af5b
-        return SF_TTFORMAT;
64af5b
+        // Bitmap font, accept for now.
64af5b
+        t->goffsets = static_cast<sal_uInt32 *>(calloc(1+t->nglyphs, sizeof(sal_uInt32)));
64af5b
+        /* TODO: implement to get subsetting */
64af5b
+        assert(t->goffsets != nullptr);
64af5b
     }
64af5b
 
64af5b
     table = getTable(t, O_hhea);
64af5b
diff --git a/vcl/unx/generic/fontmanager/fontconfig.cxx b/vcl/unx/generic/fontmanager/fontconfig.cxx
64af5b
index 2c16e040cdab..33c50d082912 100644
64af5b
--- a/vcl/unx/generic/fontmanager/fontconfig.cxx
64af5b
+++ b/vcl/unx/generic/fontmanager/fontconfig.cxx
64af5b
@@ -67,7 +67,7 @@ namespace
64af5b
 
64af5b
 class FontCfgWrapper
64af5b
 {
64af5b
-    FcFontSet* m_pOutlineSet;
64af5b
+    FcFontSet* m_pFontSet;
64af5b
 
64af5b
     void addFontSet( FcSetName );
64af5b
 
64af5b
@@ -95,19 +95,15 @@ private:
64af5b
 };
64af5b
 
64af5b
 FontCfgWrapper::FontCfgWrapper()
64af5b
-    :
64af5b
-        m_pOutlineSet( nullptr ),
64af5b
-        m_pLanguageTag( nullptr )
64af5b
+    : m_pFontSet(nullptr)
64af5b
+    , m_pLanguageTag(nullptr)
64af5b
 {
64af5b
     FcInit();
64af5b
 }
64af5b
 
64af5b
 void FontCfgWrapper::addFontSet( FcSetName eSetName )
64af5b
 {
64af5b
-    /*
64af5b
-      add only acceptable outlined fonts to our config,
64af5b
-      for future fontconfig use
64af5b
-    */
64af5b
+    // Add only acceptable fonts to our config, for future fontconfig use.
64af5b
     FcFontSet* pOrig = FcConfigGetFonts( FcConfigGetCurrent(), eSetName );
64af5b
     if( !pOrig )
64af5b
         return;
64af5b
@@ -116,10 +112,12 @@ void FontCfgWrapper::addFontSet( FcSetName eSetName )
64af5b
     for( int i = 0; i < pOrig->nfont; ++i )
64af5b
     {
64af5b
         FcPattern* pPattern = pOrig->fonts[i];
64af5b
-        // #i115131# ignore non-outline fonts
64af5b
-        FcBool bOutline = FcFalse;
64af5b
-        FcResult eOutRes = FcPatternGetBool( pPattern, FC_OUTLINE, 0, &bOutline );
64af5b
-        if( (eOutRes != FcResultMatch) || (bOutline == FcFalse) )
64af5b
+        // #i115131# ignore non-scalable fonts
64af5b
+        // Scalable fonts are usually outline fonts, but some bitmaps fonts
64af5b
+        // (like Noto Color Emoji) are also scalable.
64af5b
+        FcBool bScalable = FcFalse;
64af5b
+        FcResult eScalableRes = FcPatternGetBool(pPattern, FC_SCALABLE, 0, &bScalable);
64af5b
+        if ((eScalableRes != FcResultMatch) || (bScalable == FcFalse))
64af5b
             continue;
64af5b
 
64af5b
         // Ignore Type 1 fonts, too.
64af5b
@@ -129,7 +127,7 @@ void FontCfgWrapper::addFontSet( FcSetName eSetName )
64af5b
             continue;
64af5b
 
64af5b
         FcPatternReference( pPattern );
64af5b
-        FcFontSetAdd( m_pOutlineSet, pPattern );
64af5b
+        FcFontSetAdd( m_pFontSet, pPattern );
64af5b
     }
64af5b
 
64af5b
     // TODO?: FcFontSetDestroy( pOrig );
64af5b
@@ -220,16 +218,16 @@ namespace
64af5b
 
64af5b
 FcFontSet* FontCfgWrapper::getFontSet()
64af5b
 {
64af5b
-    if( !m_pOutlineSet )
64af5b
+    if( !m_pFontSet )
64af5b
     {
64af5b
-        m_pOutlineSet = FcFontSetCreate();
64af5b
+        m_pFontSet = FcFontSetCreate();
64af5b
         addFontSet( FcSetSystem );
64af5b
         addFontSet( FcSetApplication );
64af5b
 
64af5b
-        ::std::sort(m_pOutlineSet->fonts,m_pOutlineSet->fonts+m_pOutlineSet->nfont,SortFont());
64af5b
+        ::std::sort(m_pFontSet->fonts,m_pFontSet->fonts+m_pFontSet->nfont,SortFont());
64af5b
     }
64af5b
 
64af5b
-    return m_pOutlineSet;
64af5b
+    return m_pFontSet;
64af5b
 }
64af5b
 
64af5b
 FontCfgWrapper::~FontCfgWrapper()
64af5b
@@ -376,10 +374,10 @@ void FontCfgWrapper::clear()
64af5b
 {
64af5b
     m_aFontNameToLocalized.clear();
64af5b
     m_aLocalizedToCanonical.clear();
64af5b
-    if( m_pOutlineSet )
64af5b
+    if( m_pFontSet )
64af5b
     {
64af5b
-        FcFontSetDestroy( m_pOutlineSet );
64af5b
-        m_pOutlineSet = nullptr;
64af5b
+        FcFontSetDestroy( m_pFontSet );
64af5b
+        m_pFontSet = nullptr;
64af5b
     }
64af5b
     delete m_pLanguageTag;
64af5b
     m_pLanguageTag = nullptr;
64af5b
@@ -499,7 +497,7 @@ void PrintFontManager::countFontconfigFonts( std::unordered_map<OString, int>& o
64af5b
             int width = 0;
64af5b
             int spacing = 0;
64af5b
             int nCollectionEntry = -1;
64af5b
-            FcBool outline = false;
64af5b
+            FcBool scalable = false;
64af5b
 
64af5b
             FcResult eFileRes         = FcPatternGetString(pFSet->fonts[i], FC_FILE, 0, &file;;
64af5b
             FcResult eFamilyRes       = rWrapper.LocalizedElementFromPattern( pFSet->fonts[i], &family, FC_FAMILY, FC_FAMILYLANG );
64af5b
@@ -510,11 +508,11 @@ void PrintFontManager::countFontconfigFonts( std::unordered_map<OString, int>& o
64af5b
             FcResult eWeightRes       = FcPatternGetInteger(pFSet->fonts[i], FC_WEIGHT, 0, &weight);
64af5b
             FcResult eWidthRes        = FcPatternGetInteger(pFSet->fonts[i], FC_WIDTH, 0, &width);
64af5b
             FcResult eSpacRes         = FcPatternGetInteger(pFSet->fonts[i], FC_SPACING, 0, &spacing);
64af5b
-            FcResult eOutRes          = FcPatternGetBool(pFSet->fonts[i], FC_OUTLINE, 0, &outline);
64af5b
+            FcResult eScalableRes     = FcPatternGetBool(pFSet->fonts[i], FC_SCALABLE, 0, &scalable);
64af5b
             FcResult eIndexRes        = FcPatternGetInteger(pFSet->fonts[i], FC_INDEX, 0, &nCollectionEntry);
64af5b
             FcResult eFormatRes       = FcPatternGetString(pFSet->fonts[i], FC_FONTFORMAT, 0, &format);
64af5b
 
64af5b
-            if( eFileRes != FcResultMatch || eFamilyRes != FcResultMatch || eOutRes != FcResultMatch )
64af5b
+            if( eFileRes != FcResultMatch || eFamilyRes != FcResultMatch || eScalableRes != FcResultMatch )
64af5b
                 continue;
64af5b
 
64af5b
 #if (OSL_DEBUG_LEVEL > 2)
64af5b
@@ -528,14 +526,15 @@ void PrintFontManager::countFontconfigFonts( std::unordered_map<OString, int>& o
64af5b
                      , eWeightRes == FcResultMatch ? width : -1
64af5b
                      , eSpacRes == FcResultMatch ? spacing : -1
64af5b
                      , eOutRes == FcResultMatch ? outline : -1
64af5b
+                     , eScalableRes == FcResultMatch ? scalable : -1
64af5b
                      , eFormatRes == FcResultMatch ? (const char*)format : "<unknown>"
64af5b
                      );
64af5b
 #endif
64af5b
 
64af5b
-//            OSL_ASSERT(eOutRes != FcResultMatch || outline);
64af5b
+//            OSL_ASSERT(eScalableRes != FcResultMatch || scalable);
64af5b
 
64af5b
-            // only outline fonts are usable to psprint anyway
64af5b
-            if( eOutRes == FcResultMatch && ! outline )
64af5b
+            // only scalable fonts are usable to psprint anyway
64af5b
+            if( eScalableRes == FcResultMatch && ! scalable )
64af5b
                 continue;
64af5b
 
64af5b
             if (isPreviouslyDuplicateOrObsoleted(pFSet, i))
64af5b
@@ -807,6 +806,11 @@ namespace
64af5b
 #endif
64af5b
     }
64af5b
 
64af5b
+    bool isEmoji(sal_uInt32 nCurrentChar)
64af5b
+    {
64af5b
+        return u_hasBinaryProperty(nCurrentChar, UCHAR_EMOJI);
64af5b
+    }
64af5b
+
64af5b
     //returns true if the given code-point couldn't possibly be in rLangTag.
64af5b
     bool isImpossibleCodePointForLang(const LanguageTag &rLangTag, sal_uInt32 currentChar)
64af5b
     {
64af5b
@@ -855,6 +859,8 @@ namespace
64af5b
 
64af5b
     OUString getExemplarLangTagForCodePoint(sal_uInt32 currentChar)
64af5b
     {
64af5b
+        if (isEmoji(currentChar))
64af5b
+            return "und-zsye";
64af5b
         int32_t script = u_getIntPropertyValue(currentChar, UCHAR_SCRIPT);
64af5b
         UScriptCode eScript = static_cast<UScriptCode>(script);
64af5b
         OStringBuffer aBuf(unicode::getExemplarLanguageForUScriptCode(eScript));
64af5b
@@ -981,7 +987,7 @@ void PrintFontManager::Substitute( FontSelectPattern &rPattern, OUString& rMissi
64af5b
             FcCharSetAddChar( codePoints, nCode );
64af5b
             //if the codepoint is impossible for this lang tag, then clear it
64af5b
             //and autodetect something useful
64af5b
-            if (!aLangAttrib.isEmpty() && isImpossibleCodePointForLang(aLangTag, nCode))
64af5b
+            if (!aLangAttrib.isEmpty() && (isImpossibleCodePointForLang(aLangTag, nCode) || isEmoji(nCode)))
64af5b
                 aLangAttrib.clear();
64af5b
             //#i105784#/rhbz#527719  improve selection of fallback font
64af5b
             if (aLangAttrib.isEmpty())
64af5b
diff --git a/vcl/unx/generic/glyphs/freetype_glyphcache.cxx b/vcl/unx/generic/glyphs/freetype_glyphcache.cxx
64af5b
index 5a55ee47bff3..0b03f428c3fa 100644
64af5b
--- a/vcl/unx/generic/glyphs/freetype_glyphcache.cxx
64af5b
+++ b/vcl/unx/generic/glyphs/freetype_glyphcache.cxx
64af5b
@@ -409,9 +409,9 @@ FreetypeFont::FreetypeFont( const FontSelectPattern& rFSD, FreetypeFontInfo* pFI
64af5b
 
64af5b
     FT_New_Size( maFaceFT, &maSizeFT );
64af5b
     FT_Activate_Size( maSizeFT );
64af5b
-    FT_Error rc = FT_Set_Pixel_Sizes( maFaceFT, mnWidth, rFSD.mnHeight );
64af5b
-    if( rc != FT_Err_Ok )
64af5b
-        return;
64af5b
+    /* This might fail for color bitmap fonts, but that is fine since we will
64af5b
+     * not need any glyph data from FreeType in this case */
64af5b
+    /*FT_Error rc = */ FT_Set_Pixel_Sizes( maFaceFT, mnWidth, rFSD.mnHeight );
64af5b
 
64af5b
     FT_Select_Charmap(maFaceFT, FT_ENCODING_UNICODE);
64af5b
 
64af5b
diff --git a/vcl/unx/gtk3/gtk3gtkframe.cxx b/vcl/unx/gtk3/gtk3gtkframe.cxx
64af5b
index 4ee63a98da95..2f80d03f542b 100644
64af5b
--- a/vcl/unx/gtk3/gtk3gtkframe.cxx
64af5b
+++ b/vcl/unx/gtk3/gtk3gtkframe.cxx
64af5b
@@ -4031,34 +4031,59 @@ void GtkSalFrame::IMHandler::signalIMPreeditChanged( GtkIMContext*, gpointer im_
64af5b
     pThis->m_bPreeditJustChanged = true;
64af5b
 
64af5b
     bool bEndPreedit = (!pText || !*pText) && pThis->m_aInputEvent.mpTextAttr != nullptr;
64af5b
-    pThis->m_aInputEvent.maText             = pText ? OUString( pText, strlen(pText), RTL_TEXTENCODING_UTF8 ) : OUString();
64af5b
-    pThis->m_aInputEvent.mnCursorPos        = nCursorPos;
64af5b
-    pThis->m_aInputEvent.mnCursorFlags      = 0;
64af5b
+    gint nUtf8Len = pText ? strlen(pText) : 0;
64af5b
+    pThis->m_aInputEvent.maText             = pText ? OUString(pText, nUtf8Len, RTL_TEXTENCODING_UTF8) : OUString();
64af5b
+    const OUString& rText = pThis->m_aInputEvent.maText;
64af5b
 
64af5b
-    pThis->m_aInputFlags = std::vector<ExtTextInputAttr>( std::max( 1, (int)pThis->m_aInputEvent.maText.getLength() ), ExtTextInputAttr::NONE );
64af5b
+    std::vector<sal_Int32> aUtf16Offsets;
64af5b
+    for (sal_Int32 nUtf16Offset = 0; nUtf16Offset < rText.getLength(); rText.iterateCodePoints(&nUtf16Offset))
64af5b
+        aUtf16Offsets.push_back(nUtf16Offset);
64af5b
+
64af5b
+    sal_Int32 nUtf32Len = aUtf16Offsets.size();
64af5b
+    aUtf16Offsets.push_back(rText.getLength());
64af5b
+
64af5b
+    // sanitize the CurPos which is in utf-32
64af5b
+    if (nCursorPos < 0)
64af5b
+        nCursorPos = 0;
64af5b
+    else if (nCursorPos > nUtf32Len)
64af5b
+        nCursorPos = nUtf32Len;
64af5b
+
64af5b
+    pThis->m_aInputEvent.mnCursorPos = aUtf16Offsets[nCursorPos];
64af5b
+    pThis->m_aInputEvent.mnCursorFlags = 0;
64af5b
+
64af5b
+    pThis->m_aInputFlags = std::vector<ExtTextInputAttr>( std::max( 1, static_cast<int>(rText.getLength()) ), ExtTextInputAttr::NONE );
64af5b
 
64af5b
     PangoAttrIterator *iter = pango_attr_list_get_iterator(pAttrs);
64af5b
     do
64af5b
     {
64af5b
         GSList *attr_list = nullptr;
64af5b
         GSList *tmp_list = nullptr;
64af5b
-        gint start, end;
64af5b
+        gint nUtf8Start, nUtf8End;
64af5b
         ExtTextInputAttr sal_attr = ExtTextInputAttr::NONE;
64af5b
 
64af5b
-        pango_attr_iterator_range (iter, &start, &end;;
64af5b
-        if (start == G_MAXINT || end == G_MAXINT)
64af5b
-        {
64af5b
-            auto len = pText ? g_utf8_strlen(pText, -1) : 0;
64af5b
-            if (end == G_MAXINT)
64af5b
-                end = len;
64af5b
-            if (start == G_MAXINT)
64af5b
-                start = len;
64af5b
-        }
64af5b
-        if (end == start)
64af5b
+        // docs say... "Get the range of the current segment ... the stored
64af5b
+        // return values are signed, not unsigned like the values in
64af5b
+        // PangoAttribute", which implies that the units are otherwise the same
64af5b
+        // as that of PangoAttribute whose docs state these units are "in
64af5b
+        // bytes"
64af5b
+        // so this is the utf8 range
64af5b
+        pango_attr_iterator_range(iter, &nUtf8Start, &nUtf8End);
64af5b
+
64af5b
+        // sanitize the utf8 range
64af5b
+        nUtf8Start = std::min(nUtf8Start, nUtf8Len);
64af5b
+        nUtf8End = std::min(nUtf8End, nUtf8Len);
64af5b
+        if (nUtf8Start >= nUtf8End)
64af5b
             continue;
64af5b
 
64af5b
-        start = g_utf8_pointer_to_offset (pText, pText + start);
64af5b
-        end = g_utf8_pointer_to_offset (pText, pText + end);
64af5b
+        // get the utf32 range
64af5b
+        sal_Int32 nUtf32Start = g_utf8_pointer_to_offset(pText, pText + nUtf8Start);
64af5b
+        sal_Int32 nUtf32End = g_utf8_pointer_to_offset(pText, pText + nUtf8End);
64af5b
+
64af5b
+        // sanitize the utf32 range
64af5b
+        nUtf32Start = std::min(nUtf32Start, nUtf32Len);
64af5b
+        nUtf32End = std::min(nUtf32End, nUtf32Len);
64af5b
+        if (nUtf32Start >= nUtf32End)
64af5b
+            continue;
64af5b
 
64af5b
         tmp_list = attr_list = pango_attr_iterator_get_attrs (iter);
64af5b
         while (tmp_list)
64af5b
@@ -4088,11 +4113,12 @@ void GtkSalFrame::IMHandler::signalIMPreeditChanged( GtkIMContext*, gpointer im_
64af5b
         g_slist_free (attr_list);
64af5b
 
64af5b
         // Set the sal attributes on our text
64af5b
-        for (int i = start; i < end; ++i)
64af5b
+        // rhbz#1648281 apply over our utf-16 range derived from the input utf-32 range
64af5b
+        for (sal_Int32 i = aUtf16Offsets[nUtf32Start]; i < aUtf16Offsets[nUtf32End]; ++i)
64af5b
         {
64af5b
             SAL_WARN_IF(i >= static_cast<int>(pThis->m_aInputFlags.size()),
64af5b
                 "vcl.gtk3", "pango attrib out of range. Broken range: "
64af5b
-                << start << "," << end << " Legal range: 0,"
64af5b
+                << aUtf16Offsets[nUtf32Start] << "," << aUtf16Offsets[nUtf32End] << " Legal range: 0,"
64af5b
                 << pThis->m_aInputFlags.size());
64af5b
             if (i >= static_cast<int>(pThis->m_aInputFlags.size()))
64af5b
                 continue;
64af5b
-- 
64af5b
2.21.0
64af5b