void FontCfgWrapper::addFontSet( FcSetName eSetName )
{ // Add only acceptable fonts to our config, for future fontconfig use.
FcFontSet* pOrig = FcConfigGetFonts( FcConfigGetCurrent(), eSetName ); if( !pOrig ) return;
// filter the font sets to remove obsolete faces for( int i = 0; i < pOrig->nfont; ++i )
{
FcPattern* pPattern = pOrig->fonts[i]; // #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;
if (bHaveA && bHaveB) return strcmp(reinterpret_cast<constchar*>(pNameA), reinterpret_cast<constchar*>(pNameB));
returnint(bHaveA) - int(bHaveB);
}
//Sort fonts so that fonts with the same family name are side-by-side, with //those with higher version numbers first class SortFont
{ public: booloperator()(const FcPattern *a, const FcPattern *b)
{ int comp = compareFontNames(a, b); if (comp != 0) return comp < 0;
if (bHaveA && bHaveB) return nVersionA > nVersionB;
return bHaveA > bHaveB;
}
};
//See fdo#30729 for where an old opensymbol installed system-wide can //clobber the new opensymbol installed locally
//See if this font is a duplicate with equal attributes which has already been //inserted, or if it an older version of an inserted fonts. Depends on FcFontSet //being sorted with SortFont bool isPreviouslyDuplicateOrObsoleted(FcFontSet const *pFSet, int i)
{ const FcPattern *a = pFSet->fonts[i];
FcFontSet* FontCfgWrapper::getFontSet()
{ if( !m_pFontSet )
{
m_pFontSet = FcFontSetCreate(); #if HAVE_MORE_FONTS
m_bRestrictFontSetToApplicationFonts = [] { return getenv("SAL_NON_APPLICATION_FONT_USE") != nullptr;
}(); #endif // Add the application fonts before the system fonts. // tdf#157939 We will remove duplicate fonts, where the duplicate is // the one with a smaller version number. If the same version font is // available system-wide or bundled with our application, then we // prefer via stable-sort the first one we see. Load application fonts // first to prefer the one we bundle in the application in that case.
addFontSet( FcSetApplication ); if (!m_bRestrictFontSetToApplicationFonts)
addFontSet( FcSetSystem );
FcChar8* bestname(const std::vector<lang_and_element> &elements, const LanguageTag & rLangTag)
{
FcChar8* candidate = elements.begin()->second; /* FIXME-BCP47: once fontconfig supports language tags this
* language-territory stuff needs to be changed! */
SAL_INFO_IF( !rLangTag.isIsoLocale(), "vcl.fonts", "localizedsorter::bestname - not an ISO locale");
OString sLangMatch(OUStringToOString(rLangTag.getLanguage().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8));
OString sFullMatch = sLangMatch + "-" +
OUStringToOString(rLangTag.getCountry().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8);
bool alreadyclosematch = false; bool found_fallback_englishname = false; for (autoconst& element : elements)
{ constchar *pLang = reinterpret_cast<constchar*>(element.first); if( sFullMatch == pLang)
{ // both language and country match
candidate = element.second; break;
} elseif( alreadyclosematch )
{ // current candidate matches lang of lang-TERRITORY // override candidate only if there is a full match continue;
} elseif( sLangMatch == pLang)
{ // just the language matches
candidate = element.second;
alreadyclosematch = true;
} elseif( found_fallback_englishname )
{ // already found an english fallback, don't override candidate // unless there is a better language match continue;
} elseif( rtl_str_compare( pLang, "en") == 0)
{ // select a fallback candidate of the first english element // name
candidate = element.second;
found_fallback_englishname = true;
}
} return candidate;
}
}
//Set up maps to quickly map between a fonts best UI name and all the rest of its names, and vice versa void FontCfgWrapper::cacheLocalizedFontNames(const FcChar8 *origfontname, const FcChar8 *bestfontname, const std::vector< lang_and_element > &lang_and_elements)
{ for (autoconst& element : lang_and_elements)
{ constchar *candidate = reinterpret_cast<constchar*>(element.second); if (rtl_str_compare(candidate, reinterpret_cast<constchar*>(bestfontname)) != 0)
m_aFontNameToLocalized[OString(candidate)] = OString(reinterpret_cast<constchar*>(bestfontname));
} if (rtl_str_compare(reinterpret_cast<constchar*>(origfontname), reinterpret_cast<constchar*>(bestfontname)) != 0)
m_aLocalizedToCanonical[OString(reinterpret_cast<constchar*>(bestfontname))] = OString(reinterpret_cast<constchar*>(origfontname));
}
if( eElementRes == FcResultMatch)
{
FcChar8* elementlang = nullptr; if (FcPatternGetString( pPattern, elementlangtype, 0, &elementlang ) == FcResultMatch)
{
std::vector< lang_and_element > lang_and_elements;
lang_and_elements.emplace_back(elementlang, *element); int k = 1; while (true)
{ if (FcPatternGetString( pPattern, elementlangtype, k, &elementlang ) != FcResultMatch) break; if (FcPatternGetString( pPattern, elementtype, k, element ) != FcResultMatch) break;
lang_and_elements.emplace_back(elementlang, *element);
++k;
}
if (!m_pLanguageTag)
m_pLanguageTag.reset(new LanguageTag(SvtSysLocaleOptions().GetRealUILanguageTag()));
// FontConfig orders Typographic Family/Subfamily before old // R/B/I/BI-compatible ones, but we want the later, so reverse the // names to match them first.
std::reverse(lang_and_elements.begin(), lang_and_elements.end());
//if this element is a fontname, map the other names to this best-name if (rtl_str_compare(elementtype, FC_FAMILY) == 0)
cacheLocalizedFontNames(origelement, *element, lang_and_elements);
}
}
namespace
{ // for variable fonts, FC_INDEX has been changed such that the lower half is now the // index of the font within the collection, and the upper half has been repurposed // as the index within the variations unsignedint GetCollectionIndex(unsignedint nEntryId)
{ return nEntryId & 0xFFFF;
}
for( int i = 0; i < pFSet->nfont; i++ )
{
FcChar8* file = nullptr;
FcChar8* family = nullptr;
FcChar8* style = nullptr;
FcChar8* format = nullptr; int slant = 0; int weight = 0; int width = 0; int spacing = 0; int symbol = 0; int nEntryId = -1;
FcBool scalable = false;
SAL_INFO("vcl.fonts.detail", "inserted font " << family << " as fontID " << nFontID);
}
// tdf#157939 if we drop fonts, drop them from the FcConfig set too so they are not // candidates for suggestions by fontconfig if (pFSet->nfont != pFilteredSet->nfont)
rWrapper.replaceFontSet(pFilteredSet); else
FcFontSetDestroy(pFilteredSet);
}
// how does one get rid of the config ?
SAL_INFO("vcl.fonts", "inserted " << nFonts << " fonts from fontconfig");
}
// FIXME: we want to add only the newly added font not re-add the whole // application font set.
FontCfgWrapper& rWrapper = FontCfgWrapper::get();
rWrapper.addFontSet( FcSetApplication );
}
staticvoid addtopattern(FcPattern *pPattern,
FontItalic eItalic, FontWeight eWeight, FontWidth eWidth, FontPitch ePitch)
{ if( eItalic != ITALIC_DONTKNOW )
{ int nSlant = FC_SLANT_ROMAN; switch( eItalic )
{ case ITALIC_NORMAL:
nSlant = FC_SLANT_ITALIC; break; case ITALIC_OBLIQUE:
nSlant = FC_SLANT_OBLIQUE; break; default: break;
}
FcPatternAddInteger(pPattern, FC_SLANT, nSlant);
} if( eWeight != WEIGHT_DONTKNOW )
{ int nWeight = FC_WEIGHT_NORMAL; switch( eWeight )
{ case WEIGHT_THIN: nWeight = FC_WEIGHT_THIN;break; case WEIGHT_ULTRALIGHT: nWeight = FC_WEIGHT_ULTRALIGHT;break; case WEIGHT_LIGHT: nWeight = FC_WEIGHT_LIGHT;break; case WEIGHT_SEMILIGHT: nWeight = FC_WEIGHT_BOOK;break; case WEIGHT_NORMAL: nWeight = FC_WEIGHT_NORMAL;break; case WEIGHT_MEDIUM: nWeight = FC_WEIGHT_MEDIUM;break; case WEIGHT_SEMIBOLD: nWeight = FC_WEIGHT_SEMIBOLD;break; case WEIGHT_BOLD: nWeight = FC_WEIGHT_BOLD;break; case WEIGHT_ULTRABOLD: nWeight = FC_WEIGHT_ULTRABOLD;break; case WEIGHT_BLACK: nWeight = FC_WEIGHT_BLACK;break; default: break;
}
FcPatternAddInteger(pPattern, FC_WEIGHT, nWeight);
} if( eWidth != WIDTH_DONTKNOW )
{ int nWidth = FC_WIDTH_NORMAL; switch( eWidth )
{ case WIDTH_ULTRA_CONDENSED: nWidth = FC_WIDTH_ULTRACONDENSED;break; case WIDTH_EXTRA_CONDENSED: nWidth = FC_WIDTH_EXTRACONDENSED;break; case WIDTH_CONDENSED: nWidth = FC_WIDTH_CONDENSED;break; case WIDTH_SEMI_CONDENSED: nWidth = FC_WIDTH_SEMICONDENSED;break; case WIDTH_NORMAL: nWidth = FC_WIDTH_NORMAL;break; case WIDTH_SEMI_EXPANDED: nWidth = FC_WIDTH_SEMIEXPANDED;break; case WIDTH_EXPANDED: nWidth = FC_WIDTH_EXPANDED;break; case WIDTH_EXTRA_EXPANDED: nWidth = FC_WIDTH_EXTRAEXPANDED;break; case WIDTH_ULTRA_EXPANDED: nWidth = FC_WIDTH_ULTRAEXPANDED;break; default: break;
}
FcPatternAddInteger(pPattern, FC_WIDTH, nWidth);
} if( ePitch == PITCH_DONTKNOW ) return;
int nSpacing = FC_PROPORTIONAL; switch( ePitch )
{ case PITCH_FIXED: nSpacing = FC_MONO;break; case PITCH_VARIABLE: nSpacing = FC_PROPORTIONAL;break; default: break;
}
FcPatternAddInteger(pPattern, FC_SPACING, nSpacing); if (nSpacing == FC_MONO)
FcPatternAddString(pPattern, FC_FAMILY, reinterpret_cast<FcChar8 const *>("monospace"));
}
//returns true if the given code-point couldn't possibly be in rLangTag. bool isImpossibleCodePointForLang(const LanguageTag &rLangTag, sal_uInt32 currentChar)
{ //a non-default script is set, let's believe it if (rLangTag.hasScript()) returnfalse;
int32_t script = u_getIntPropertyValue(currentChar, UCHAR_SCRIPT);
UScriptCode eScript = static_cast<UScriptCode>(script); bool bIsImpossible = false;
OUString sLang = rLangTag.getLanguage(); switch (eScript)
{ //http://en.wiktionary.org/wiki/Category:Oriya_script_languages case USCRIPT_ORIYA:
bIsImpossible =
sLang != "or" &&
sLang != "kxv"; break; //http://en.wiktionary.org/wiki/Category:Telugu_script_languages case USCRIPT_TELUGU:
bIsImpossible =
sLang != "te" &&
sLang != "gon" &&
sLang != "kfc"; break; //http://en.wiktionary.org/wiki/Category:Bengali_script_languages case USCRIPT_BENGALI:
bIsImpossible =
sLang != "bn" &&
sLang != "as" &&
sLang != "bpy" &&
sLang != "ctg" &&
sLang != "sa"; break; default: break;
}
SAL_WARN_IF(bIsImpossible, "vcl.fonts", "In glyph fallback throwing away the language property of "
<< sLang << " because the detected script for '0x"
<< OUString::number(currentChar, 16)
<< "' is " << uscript_getName(eScript)
<< " and that language doesn't make sense. Autodetecting instead."); return bIsImpossible;
}
IMPL_LINK_NOARG(PrintFontManager, autoInstallFontLangSupport, Timer *, void)
{ try
{ usingnamespace org::freedesktop::PackageKit;
css::uno::Reference<XSyncDbusSessionHelper> xSyncDbusSessionHelper(SyncDbusSessionHelper::create(comphelper::getProcessComponentContext()));
xSyncDbusSessionHelper->InstallFontconfigResources(comphelper::containerToSequence(m_aCurrentRequests), u"hide-finished"_ustr);
} catch (const css::uno::Exception&)
{
TOOLS_INFO_EXCEPTION("vcl.fonts", "InstallFontconfigResources problem"); // Disable this method from now on. It's simply not available on some systems // and leads to an error dialog being shown each time this is called tdf#104883
std::shared_ptr<comphelper::ConfigurationChanges> batch( comphelper::ConfigurationChanges::create() );
officecfg::Office::Common::PackageKit::EnableFontInstallation::set(false, batch);
batch->commit();
}
// Try to map tools FontFamily to fontconfig FC_FAMILY. Note that FcPatternAddString() appends // to a list, so it won't overwrite the previous FcPatternAddString(FC_FAMILY), this way we can // express that we wanted a certain font, and otherwise a given family style.
FontFamily eFamilyType = rPattern.GetFamilyType(); switch (eFamilyType)
{ case FAMILY_ROMAN:
FcPatternAddString(pPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>("serif")); break; case FAMILY_SWISS:
FcPatternAddString(pPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>("sans")); break; default: break;
}
// Add required Unicode characters, if any if ( !rMissingCodes.isEmpty() )
{
FcCharSet *codePoints = FcCharSetCreate();
bMissingJustBullet = rMissingCodes.getLength() == 1 && rMissingCodes[0] == 0xb7; for( sal_Int32 nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
{ // also handle unicode surrogates const sal_uInt32 nCode = rMissingCodes.iterateCodePoints( &nStrIndex );
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) || isEmoji(nCode)))
aLangAttrib.clear(); //#i105784#/rhbz#527719 improve selection of fallback font if (aLangAttrib.isEmpty())
{
aLangTag.reset(getExemplarLangTagForCodePoint(nCode));
aLangAttrib = mapToFontConfigLangTag(aLangTag);
}
}
FcPatternAddCharSet(pPattern, FC_CHARSET, codePoints);
FcCharSetDestroy(codePoints);
}
if (!aLangAttrib.isEmpty())
FcPatternAddString(pPattern, FC_LANG, reinterpret_cast<FcChar8 const *>(aLangAttrib.getStr()));
// bodge: testTdf153440 wants a fallback to an emoji font it adds as a temp // testing font which has the required glyphs, but that emoji font is not // seen as a "color" font, while it is possible that OpenDyslexic can be // bundled, which *is* a "color" font. The default rules (See in Fedora 38 // at least) then prefer a color font *without* the glyphs over a non-color // font *with* the glyphs, which seems like a bug to me. // Maybe this is an attempt to prefer color emoji fonts over non-color emoji // containing fonts like Symbola which has gone awry? // For testing purposes (isRestrictingFontSetForTesting is true) force a // preference for non-color fonts. if (rWrapper.isRestrictingFontSetForTesting())
FcPatternAddBool(pPattern, FC_COLOR, FcFalse);
SAL_WARN_IF(!bRet, "vcl.fonts", "no FC_FILE found, falling back to name search");
if (!bRet)
{
FcChar8* family = nullptr;
FcResult eFamilyRes = FcPatternGetString( pSet->fonts[0], FC_FAMILY, 0, &family );
// get the family name if( eFamilyRes == FcResultMatch )
{
OString sFamily(reinterpret_cast<char*>(family));
std::unordered_map< OString, OString >::const_iterator aI =
rWrapper.m_aFontNameToLocalized.find(sFamily); if (aI != rWrapper.m_aFontNameToLocalized.end())
sFamily = aI->second;
rPattern.maSearchName = OStringToOUString( sFamily, RTL_TEXTENCODING_UTF8 );
bRet = true;
}
}
if (bRet)
{ int val = 0; if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_WEIGHT, 0, &val))
rPattern.SetWeight( convertWeight(val) ); if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_SLANT, 0, &val))
rPattern.SetItalic( convertSlant(val) ); if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_SPACING, 0, &val))
rPattern.SetPitch ( convertSpacing(val) ); if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_WIDTH, 0, &val))
rPattern.SetWidthType ( convertWidth(val) );
FcBool bEmbolden; if (FcResultMatch == FcPatternGetBool(pSet->fonts[0], FC_EMBOLDEN, 0, &bEmbolden))
rPattern.mbEmbolden = bEmbolden;
FcMatrix *pMatrix = nullptr; if (FcResultMatch == FcPatternGetMatrix(pSet->fonts[0], FC_MATRIX, 0, &pMatrix))
{
rPattern.maItalicMatrix.xx = pMatrix->xx;
rPattern.maItalicMatrix.xy = pMatrix->xy;
rPattern.maItalicMatrix.yx = pMatrix->yx;
rPattern.maItalicMatrix.yy = pMatrix->yy;
}
}
// update rMissingCodes by removing resolved code points if( !rMissingCodes.isEmpty() )
{
std::unique_ptr<sal_uInt32[]> const pRemainingCodes(new sal_uInt32[rMissingCodes.getLength()]); int nRemainingLen = 0;
FcCharSet* codePoints; if (!FcPatternGetCharSet(pSet->fonts[0], FC_CHARSET, 0, &codePoints))
{ for( sal_Int32 nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
{ // also handle surrogates const sal_uInt32 nCode = rMissingCodes.iterateCodePoints( &nStrIndex ); if (FcCharSetHasChar(codePoints, nCode) != FcTrue)
pRemainingCodes[ nRemainingLen++ ] = nCode;
}
}
OUString sStillMissing(pRemainingCodes.get(), nRemainingLen); if (!Application::IsHeadlessModeEnabled() && officecfg::Office::Common::PackageKit::EnableFontInstallation::get())
{ if (sStillMissing == rMissingCodes) //replaced nothing
{ //It'd be better if we could ask packagekit using the //missing codepoints or some such rather than using //"language" as a proxy to how fontconfig considers //scripts to default to a given language. for (sal_Int32 i = 0; i < nRemainingLen; ++i)
{
LanguageTag aOurTag(getExemplarLangTagForCodePoint(pRemainingCodes[i]));
OString sTag = OUStringToOString(aOurTag.getBcp47(), RTL_TEXTENCODING_UTF8); if (!m_aPreviousLangSupportRequests.insert(sTag).second) continue;
sTag = mapToFontConfigLangTag(aOurTag); if (!sTag.isEmpty() && m_aPreviousLangSupportRequests.find(sTag) == m_aPreviousLangSupportRequests.end())
{
OString sReq = OString::Concat(":lang=") + sTag;
m_aCurrentRequests.push_back(OUString::fromUtf8(sReq));
m_aPreviousLangSupportRequests.insert(sTag);
}
}
} if (!m_aCurrentRequests.empty())
m_aFontInstallerTimer.Start();
}
rMissingCodes = sStillMissing;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Angebot
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.