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