tdf#114192: Speed up populating font list when FontConfig is used
Instead of calling psp::PrintFontManager::analyzeFontFile() get the font
names and metadata, use the values taken from FontConfig which nowadays
provides everything we need. This speeds up startup time significantly,
especially when there is a large number of fonts installed.
This also uses the correct style name, we were mixing name id 1 (family)
with name id 17 (typographic subfamily) while we should have been using
name id 2 (subfamily). The name ids 1, 2 and 16, 17 should be used
together not mixed and matched, and we need the former because it is
compatible with the R/I/B/BI model we (and the other office suite) use.
So instead of "Foo Black, Black", we now get "Foo Black, Regular".
On a system with 6616 fonts installed. Before:
$ export OOO_EXIT_POST_STARTUP=1
$ time ./instdir/program/soffice.bin --headless
real 0m4.744s
user 0m1.672s
sys 0m2.547s
after:
$ export OOO_EXIT_POST_STARTUP=1
$ time ./instdir/program/soffice.bin --headless
real 0m1.377s
user 0m0.563s
sys 0m0.297s
Change-Id: Ib7e358f16530c2daabc7ef677ef6a148fdf08e6e
diff --git a/vcl/unx/generic/fontmanager/fontconfig.cxx b/vcl/unx/generic/fontmanager/fontconfig.cxx
index 7ceb422..1131714 100644
--- a/vcl/unx/generic/fontmanager/fontconfig.cxx
+++ b/vcl/unx/generic/fontmanager/fontconfig.cxx
@@ -446,6 +446,11 @@ FcResult FontCfgWrapper::LocalizedElementFromPattern(FcPattern const * pPattern,
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());
*element = bestname(lang_and_elements, *m_pLanguageTag);
//if this element is a fontname, map the other names to this best-name
@@ -545,19 +550,6 @@ namespace
}
}
//FontConfig doesn't come with a way to remove an element from a FontSet as far
//as I can see
static void lcl_FcFontSetRemove(FcFontSet* pFSet, int i)
{
FcPatternDestroy(pFSet->fonts[i]);
int nTail = pFSet->nfont - (i + 1);
--pFSet->nfont;
if (!nTail)
return;
memmove(pFSet->fonts + i, pFSet->fonts + i + 1, nTail*sizeof(FcPattern*));
}
namespace
{
// for variable fonts, FC_INDEX has been changed such that the lower half is now the
@@ -594,6 +586,7 @@ void PrintFontManager::countFontconfigFonts()
int weight = 0;
int width = 0;
int spacing = 0;
int symbol = 0;
int nEntryId = -1;
FcBool scalable = false;
@@ -607,10 +600,11 @@ void PrintFontManager::countFontconfigFonts()
FcResult eWidthRes = FcPatternGetInteger(pFSet->fonts[i], FC_WIDTH, 0, &width);
FcResult eSpacRes = FcPatternGetInteger(pFSet->fonts[i], FC_SPACING, 0, &spacing);
FcResult eScalableRes = FcPatternGetBool(pFSet->fonts[i], FC_SCALABLE, 0, &scalable);
FcResult eSymbolRes = FcPatternGetBool(pFSet->fonts[i], FC_SYMBOL, 0, &symbol);
FcResult eIndexRes = FcPatternGetInteger(pFSet->fonts[i], FC_INDEX, 0, &nEntryId);
FcResult eFormatRes = FcPatternGetString(pFSet->fonts[i], FC_FONTFORMAT, 0, &format);
if( eFileRes != FcResultMatch || eFamilyRes != FcResultMatch || eScalableRes != FcResultMatch )
if( eFileRes != FcResultMatch || eFamilyRes != FcResultMatch || eScalableRes != FcResultMatch || eStyleRes != FcResultMatch )
continue;
SAL_INFO(
@@ -623,11 +617,12 @@ void PrintFontManager::countFontconfigFonts()
<< (eSpacRes == FcResultMatch ? spacing : -1) << ", scalable = "
<< (eScalableRes == FcResultMatch ? scalable : -1) << ", format "
<< (eFormatRes == FcResultMatch
? reinterpret_cast<const char*>(format) : "<unknown>"));
? reinterpret_cast<const char*>(format) : "<unknown>")
<< " symbol = " << (eSymbolRes == FcResultMatch ? symbol : -1));
// OSL_ASSERT(eScalableRes != FcResultMatch || scalable);
// only scalable fonts are usable to psprint anyway
// We support only scalable fonts
if( eScalableRes == FcResultMatch && ! scalable )
continue;
@@ -637,97 +632,45 @@ void PrintFontManager::countFontconfigFonts()
continue;
}
// see if this font is already cached
// update attributes
OString aDir, aBase, aOrgPath( reinterpret_cast<char*>(file) );
splitPath( aOrgPath, aDir, aBase );
int nDirID = getDirectoryAtom( aDir );
SAL_INFO("vcl.fonts.detail", "file " << aBase << " not cached");
// not known, analyze font file to get attributes
// not described by fontconfig (e.g. alias names, PSName)
if (eFormatRes != FcResultMatch)
format = nullptr;
std::vector<PrintFont> aFonts = analyzeFontFile( nDirID, aBase, reinterpret_cast<char*>(format) );
if(aFonts.empty())
{
SAL_INFO(
"vcl.fonts", "Warning: file \"" << aOrgPath << "\" is unusable to psprint");
//remove font, reuse index
//we want to remove unusable fonts here, in case there is a usable font
//which duplicates the properties of the unusable one
//not removing the unusable font will risk the usable font being rejected
//as a duplicate by isPreviouslyDuplicateOrObsoleted
lcl_FcFontSetRemove(pFSet, i--);
continue;
PrintFont aFont;
aFont.m_nDirectory = nDirID;
aFont.m_aFontFile = aBase;
if (eIndexRes == FcResultMatch)
{
aFont.m_nCollectionEntry = GetCollectionIndex(nEntryId);
aFont.m_nVariationEntry = GetVariationIndex(nEntryId);
}
std::optional<PrintFont> xUpdate;
auto& rFA = aFont.m_aFontAttributes;
rFA.SetWeight(WEIGHT_NORMAL);
rFA.SetWidthType(WIDTH_NORMAL);
rFA.SetPitch(PITCH_VARIABLE);
rFA.SetQuality(512);
if (aFonts.size() == 1) // one font
xUpdate = aFonts.front();
else // more than one font
{
// a collection entry, get the correct index
if( eIndexRes == FcResultMatch && nEntryId != -1 )
{
int nCollectionEntry = GetCollectionIndex(nEntryId);
for (const auto & font : aFonts)
{
if( font.m_nCollectionEntry == nCollectionEntry )
{
xUpdate = font;
break;
}
}
}
rFA.SetFamilyName(OStringToOUString(std::string_view(reinterpret_cast<char*>(family)), RTL_TEXTENCODING_UTF8));
if (eStyleRes == FcResultMatch)
rFA.SetStyleName(OStringToOUString(std::string_view(reinterpret_cast<char*>(style)), RTL_TEXTENCODING_UTF8));
if (eWeightRes == FcResultMatch)
rFA.SetWeight(convertWeight(weight));
if (eWidthRes == FcResultMatch)
rFA.SetWidthType(convertWidth(width));
if (eSpacRes == FcResultMatch)
rFA.SetPitch(convertSpacing(spacing));
if (eSlantRes == FcResultMatch)
rFA.SetItalic(convertSlant(slant));
if (eSymbolRes == FcResultMatch)
rFA.SetMicrosoftSymbolEncoded(bool(symbol));
if (xUpdate)
{
// update collection entry
// additional entries will be created in the cache
// if this is a new index (that is if the loop above
// ran to the end of the list)
xUpdate->m_nCollectionEntry = GetCollectionIndex(nEntryId);
}
else
{
SAL_INFO(
"vcl.fonts",
"multiple fonts for file, but no index in fontconfig pattern ! (index res ="
<< eIndexRes << " collection entry = " << nEntryId
<< "; file will not be used");
// we have found more than one font in this file
// but fontconfig will not tell us which index is meant
// -> something is in disorder, do not use this font
}
}
if (xUpdate)
{
auto& rDFA = xUpdate->m_aFontAttributes;
// set family name
if( eWeightRes == FcResultMatch )
rDFA.SetWeight(convertWeight(weight));
if( eWidthRes == FcResultMatch )
rDFA.SetWidthType(convertWidth(width));
if( eSpacRes == FcResultMatch )
rDFA.SetPitch(convertSpacing(spacing));
if( eSlantRes == FcResultMatch )
rDFA.SetItalic(convertSlant(slant));
if( eStyleRes == FcResultMatch )
rDFA.SetStyleName(OStringToOUString( std::string_view( reinterpret_cast<char*>(style) ), RTL_TEXTENCODING_UTF8 ));
if( eIndexRes == FcResultMatch )
xUpdate->m_nVariationEntry = GetVariationIndex(nEntryId);
// sort into known fonts
fontID aFont = m_nNextFontID++;
m_aFonts.emplace( aFont, *xUpdate );
m_aFontFileToFontID[ aBase ].insert( aFont );
nFonts++;
SAL_INFO("vcl.fonts.detail", "inserted font " << family << " as fontID " << aFont);
}
// sort into known fonts
fontID nFontID = m_nNextFontID++;
m_aFonts.emplace(nFontID, aFont);
m_aFontFileToFontID[aBase].insert(nFontID);
nFonts++;
SAL_INFO("vcl.fonts.detail", "inserted font " << family << " as fontID " << nFontID);
}
}