tdf#140639 cache FcPattern for font options

Change-Id: Ibb1b1e06630e505924e05ea4b5b454e58738d743
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/114083
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
diff --git a/vcl/inc/unx/fontmanager.hxx b/vcl/inc/unx/fontmanager.hxx
index 0f5a85d..9ae95d0 100644
--- a/vcl/inc/unx/fontmanager.hxx
+++ b/vcl/inc/unx/fontmanager.hxx
@@ -313,6 +313,7 @@ public:
    void matchFont( FastPrintFontInfo& rInfo, const css::lang::Locale& rLocale );

    static std::unique_ptr<FontConfigFontOptions> getFontOptions(const FontAttributes& rFontAttributes, int nSize);
    static void clearFontOptionsCache();

    void Substitute(FontSelectPattern &rPattern, OUString& rMissingCodes);

diff --git a/vcl/unx/generic/fontmanager/fontconfig.cxx b/vcl/unx/generic/fontmanager/fontconfig.cxx
index 1ebe39c..748fbf2 100644
--- a/vcl/unx/generic/fontmanager/fontconfig.cxx
+++ b/vcl/unx/generic/fontmanager/fontconfig.cxx
@@ -20,6 +20,7 @@
#include <memory>
#include <string_view>

#include <o3tl/lru_map.hxx>
#include <unx/fontmanager.hxx>
#include <unx/helper.hxx>
#include <comphelper/sequence.hxx>
@@ -46,6 +47,7 @@ using namespace psp;

#include <osl/process.h>

#include <boost/functional/hash.hpp>
#include <utility>
#include <algorithm>

@@ -53,7 +55,92 @@ using namespace osl;

namespace
{
    typedef std::pair<FcChar8*, FcChar8*> lang_and_element;

struct FontOptionsKey
{
    OUString m_sFamilyName;
    int m_nFontSize;
    FontItalic m_eItalic;
    FontWeight m_eWeight;
    FontWidth m_eWidth;

    bool operator==(const FontOptionsKey& rOther) const
    {
        return m_sFamilyName == rOther.m_sFamilyName &&
               m_nFontSize == rOther.m_nFontSize &&
               m_eItalic == rOther.m_eItalic &&
               m_eWeight == rOther.m_eWeight &&
               m_eWidth == rOther.m_eWidth;
    }
};

}

namespace std
{

template <> struct hash<FontOptionsKey>
{
    std::size_t operator()(const FontOptionsKey& k) const noexcept
    {
        std::size_t seed = k.m_sFamilyName.hashCode();
        boost::hash_combine(seed, k.m_nFontSize);
        boost::hash_combine(seed, k.m_eItalic);
        boost::hash_combine(seed, k.m_eWeight);
        boost::hash_combine(seed, k.m_eWidth);
        return seed;
    }
};

} // end std namespace

namespace
{

struct FcPatternDeleter
{
    void operator()(FcPattern* pPattern) const
    {
        FcPatternDestroy(pPattern);
    }
};

typedef std::unique_ptr<FcPattern, FcPatternDeleter> FcPatternUniquePtr;

class CachedFontConfigFontOptions
{
private:
    FontOptionsKey m_aKey;
    std::unique_ptr<FontConfigFontOptions> m_xLastFontOptions;

    o3tl::lru_map<FontOptionsKey, FcPatternUniquePtr> lru_options_cache;

public:
    CachedFontConfigFontOptions()
        : lru_options_cache(10) // arbitrary cache size of 10
    {
    }

    std::unique_ptr<FontConfigFontOptions> lookup(const FontOptionsKey& rKey)
    {
        auto it = lru_options_cache.find(rKey);
        if (it != lru_options_cache.end())
            return std::make_unique<FontConfigFontOptions>(FcPatternDuplicate(it->second.get()));
        return nullptr;
    }

    void cache(const FontOptionsKey& rKey, const FcPattern* pPattern)
    {
        lru_options_cache.insert(std::make_pair(rKey, FcPatternUniquePtr(FcPatternDuplicate(pPattern))));
    }

    void clear()
    {
        m_xLastFontOptions.reset();
    }
};

typedef std::pair<FcChar8*, FcChar8*> lang_and_element;

class FontCfgWrapper
{
@@ -78,6 +165,7 @@ public:
//to-do, make private and add some cleaner accessor methods
    std::unordered_map< OString, OString > m_aFontNameToLocalized;
    std::unordered_map< OString, OString > m_aLocalizedToCanonical;
    CachedFontConfigFontOptions m_aCachedFontOptions;
private:
    void cacheLocalizedFontNames(const FcChar8 *origfontname, const FcChar8 *bestfontname, const std::vector< lang_and_element > &lang_and_elements);

@@ -369,6 +457,7 @@ void FontCfgWrapper::clear()
        m_pFontSet = nullptr;
    }
    m_pLanguageTag.reset();
    m_aCachedFontOptions.clear();
}

/*
@@ -1099,13 +1188,18 @@ void FontConfigFontOptions::SyncPattern(const OString& rFileName, sal_uInt32 nIn

std::unique_ptr<FontConfigFontOptions> PrintFontManager::getFontOptions(const FontAttributes& rInfo, int nSize)
{
    FontOptionsKey aKey{ rInfo.GetFamilyName(), nSize, rInfo.GetItalic(), rInfo.GetWeight(), rInfo.GetWidthType() };

    FontCfgWrapper& rWrapper = FontCfgWrapper::get();

    std::unique_ptr<FontConfigFontOptions> pOptions;
    std::unique_ptr<FontConfigFontOptions> pOptions = rWrapper.m_aCachedFontOptions.lookup(aKey);
    if (pOptions)
        return pOptions;

    FcConfig* pConfig = FcConfigGetCurrent();
    FcPattern* pPattern = FcPatternCreate();

    OString sFamily = OUStringToOString( rInfo.GetFamilyName(), RTL_TEXTENCODING_UTF8 );
    OString sFamily = OUStringToOString(aKey.m_sFamilyName, RTL_TEXTENCODING_UTF8);

    std::unordered_map< OString, OString >::const_iterator aI = rWrapper.m_aLocalizedToCanonical.find(sFamily);
    if (aI != rWrapper.m_aLocalizedToCanonical.end())
@@ -1114,7 +1208,7 @@ std::unique_ptr<FontConfigFontOptions> PrintFontManager::getFontOptions(const Fo
        FcPatternAddString(pPattern, FC_FAMILY, reinterpret_cast<FcChar8 const *>(sFamily.getStr()));

    // TODO: ePitch argument of always PITCH_DONTKNOW is suspicious
    addtopattern(pPattern, rInfo.GetItalic(), rInfo.GetWeight(), rInfo.GetWidthType(), PITCH_DONTKNOW);
    addtopattern(pPattern, aKey.m_eItalic, aKey.m_eWeight, aKey.m_eWidth, PITCH_DONTKNOW);
    FcPatternAddDouble(pPattern, FC_PIXEL_SIZE, nSize);

    FcConfigSubstitute(pConfig, pPattern, FcMatchPattern);
@@ -1124,7 +1218,10 @@ std::unique_ptr<FontConfigFontOptions> PrintFontManager::getFontOptions(const Fo
    FcResult eResult = FcResultNoMatch;
    FcFontSet* pFontSet = rWrapper.getFontSet();
    if (FcPattern* pResult = FcFontSetMatch(pConfig, &pFontSet, 1, pPattern, &eResult))
    {
        rWrapper.m_aCachedFontOptions.cache(aKey, pResult);
        pOptions.reset(new FontConfigFontOptions(pResult));
    }

    // cleanup
    FcPatternDestroy( pPattern );
@@ -1132,6 +1229,12 @@ std::unique_ptr<FontConfigFontOptions> PrintFontManager::getFontOptions(const Fo
    return pOptions;
}

void PrintFontManager::clearFontOptionsCache()
{
    FontCfgWrapper& rWrapper = FontCfgWrapper::get();
    rWrapper.m_aCachedFontOptions.clear();
}

void PrintFontManager::matchFont( FastPrintFontInfo& rInfo, const css::lang::Locale& rLocale )
{
    FontCfgWrapper& rWrapper = FontCfgWrapper::get();
diff --git a/vcl/unx/generic/gdi/freetypetextrender.cxx b/vcl/unx/generic/gdi/freetypetextrender.cxx
index 0e6556b..5f28ebd 100644
--- a/vcl/unx/generic/gdi/freetypetextrender.cxx
+++ b/vcl/unx/generic/gdi/freetypetextrender.cxx
@@ -101,6 +101,7 @@ bool FreeTypeTextRenderImpl::AddTempDevFont( PhysicalFontCollection* pFontCollec
void FreeTypeTextRenderImpl::ClearDevFontCache()
{
    FreetypeManager::get().ClearFontCache();
    psp::PrintFontManager::clearFontOptionsCache();
}

void FreeTypeTextRenderImpl::GetDevFontList( PhysicalFontCollection* pFontCollection )