include/vcl/dropcache.hxx                  |   30 ++++++++++++++++
 include/vcl/glyphitemcache.hxx             |    6 ++-
 sc/source/ui/unoobj/shapeuno.cxx           |   53 +++++++++++++++++++++--------
 sc/source/ui/view/spellcheckcontext.cxx    |   17 ++++++++-
 sw/source/core/ole/ndole.cxx               |   48 ++++++++++++++++++--------
 vcl/inc/graphic/Manager.hxx                |    7 ++-
 vcl/inc/svdata.hxx                         |    5 ++
 vcl/source/app/svapp.cxx                   |    3 -
 vcl/source/app/svdata.cxx                  |   44 ++++++++++++++++++++++++
 vcl/source/gdi/impglyphitem.cxx            |    8 ++++
 vcl/source/graphic/Manager.cxx             |    2 -
 vcl/source/outdev/textline.cxx             |   14 +++++++
 vcl/source/text/TextLayoutCache.cxx        |   51 ++++++++++++++++++++++-----
 vcl/unx/generic/fontmanager/fontconfig.cxx |   14 +++++++
 vcl/unx/generic/gdi/cairotextrender.cxx    |   35 +++++++++++++------
 15 files changed, 279 insertions(+), 58 deletions(-)

New commits:
commit f86e3c7f22d53abb9a844f6916246ed96ef99ae6
Author:     Caolán McNamara <caolan.mcnam...@collabora.com>
AuthorDate: Sun May 25 20:42:45 2025 +0100
Commit:     Caolán McNamara <caolan.mcnam...@collabora.com>
CommitDate: Fri May 30 10:32:41 2025 +0200

    register caches with ImplSVData
    
    and put them under a single trim control
    
    Change-Id: I72b53102e781f6b16968588b9066588d7fdb3055
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/185753
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>

diff --git a/include/vcl/dropcache.hxx b/include/vcl/dropcache.hxx
new file mode 100644
index 000000000000..3a9854f203f9
--- /dev/null
+++ b/include/vcl/dropcache.hxx
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <vcl/dllapi.h>
+
+namespace rtl
+{
+class OStringBuffer;
+}
+
+class VCL_DLLPUBLIC CacheOwner
+{
+protected:
+    CacheOwner();
+    virtual ~CacheOwner();
+
+public:
+    virtual void dropCaches() = 0;
+    virtual void dumpState(rtl::OStringBuffer& rState) = 0;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/include/vcl/glyphitemcache.hxx b/include/vcl/glyphitemcache.hxx
index 4abcfb6e93c7..92e427fe1aed 100644
--- a/include/vcl/glyphitemcache.hxx
+++ b/include/vcl/glyphitemcache.hxx
@@ -25,6 +25,7 @@
 
 #include <o3tl/lru_map.hxx>
 #include <vcl/glyphitem.hxx>
+#include <vcl/dropcache.hxx>
 #include <vcl/metric.hxx>
 #include <vcl/outdev.hxx>
 #include <vcl/vclptr.hxx>
@@ -38,7 +39,7 @@ Allows caching for OutputDevice::DrawText() and similar 
calls. Pass the text and
 for the call to OutputDevice::ImplLayout(). Items are cached per output device 
and its font.
 If something more changes, call clear().
 */
-class VCL_DLLPUBLIC SalLayoutGlyphsCache final
+class VCL_DLLPUBLIC SalLayoutGlyphsCache final : public CacheOwner
 {
 public:
     // NOTE: The lifetime of the returned value is guaranteed only until the 
next call
@@ -95,6 +96,9 @@ public:
     };
 
 private:
+    virtual void dropCaches() override;
+    virtual void dumpState(rtl::OStringBuffer& rState) override;
+
     struct CachedGlyphsHash
     {
         size_t operator()(const CachedGlyphsKey& key) const { return 
key.hashValue; }
diff --git a/sc/source/ui/unoobj/shapeuno.cxx b/sc/source/ui/unoobj/shapeuno.cxx
index 4aabcf03ebe0..88d31031e7ba 100644
--- a/sc/source/ui/unoobj/shapeuno.cxx
+++ b/sc/source/ui/unoobj/shapeuno.cxx
@@ -25,6 +25,7 @@
 #include <svtools/unoimap.hxx>
 #include <svx/svdobj.hxx>
 #include <svx/ImageMapInfo.hxx>
+#include <vcl/dropcache.hxx>
 #include <vcl/svapp.hxx>
 #include <vcl/unohelp.hxx>
 #include <sfx2/event.hxx>
@@ -212,6 +213,43 @@ static uno::Reference<text::XTextRange> lcl_GetTextRange( 
const uno::Reference<u
     return xRet;
 }
 
+namespace {
+
+struct PropertySetInfoCache : public CacheOwner
+{
+    uno::Reference<beans::XPropertySetInfo> getPropertySetInfo(const 
uno::Reference<beans::XPropertySetInfo>& rxPropSetInfo)
+    {
+        std::unique_lock l(gCacheMutex);
+        // prevent memory leaks, possibly we could use an LRU map here.
+        if (gCacheMap.size() > 100)
+            gCacheMap.clear();
+        auto it = gCacheMap.find(rxPropSetInfo);
+        if (it != gCacheMap.end())
+            return it->second;
+        uno::Reference<beans::XPropertySetInfo> xCombined = new 
SfxExtItemPropertySetInfo( lcl_GetShapeMap(), rxPropSetInfo->getProperties() );
+        gCacheMap.emplace(rxPropSetInfo, xCombined);
+        return xCombined;
+    }
+
+private:
+    virtual void dropCaches() override
+    {
+        std::unique_lock l(gCacheMutex);
+        gCacheMap.clear();
+    }
+
+    virtual void dumpState(rtl::OStringBuffer& rState) override
+    {
+        rState.append("
PropertySetInfoCache:   ");
+        rState.append(static_cast<sal_Int32>(gCacheMap.size()));
+    }
+
+    std::mutex gCacheMutex;
+    std::unordered_map<uno::Reference<beans::XPropertySetInfo>, 
uno::Reference<beans::XPropertySetInfo>> gCacheMap;
+};
+
+}
+
 /**
  * If there are lots of shapes, the cost of allocating the XPropertySetInfo 
structures adds up.
  * But we have a static set of properties, and most of the underlying types 
have one static
@@ -220,19 +258,8 @@ static uno::Reference<text::XTextRange> lcl_GetTextRange( 
const uno::Reference<u
  */
 static uno::Reference<beans::XPropertySetInfo> 
getPropertySetInfoFromCache(const uno::Reference<beans::XPropertySetInfo>& 
rxPropSetInfo)
 {
-    static std::mutex gCacheMutex;
-    static std::unordered_map<uno::Reference<beans::XPropertySetInfo>, 
uno::Reference<beans::XPropertySetInfo>> gCacheMap;
-
-    std::unique_lock l(gCacheMutex);
-    // prevent memory leaks, possibly we could use an LRU map here.
-    if (gCacheMap.size() > 100)
-        gCacheMap.clear();
-    auto it = gCacheMap.find(rxPropSetInfo);
-    if (it != gCacheMap.end())
-        return it->second;
-    uno::Reference<beans::XPropertySetInfo> xCombined = new 
SfxExtItemPropertySetInfo( lcl_GetShapeMap(), rxPropSetInfo->getProperties() );
-    gCacheMap.emplace(rxPropSetInfo, xCombined);
-    return xCombined;
+    static PropertySetInfoCache aCache;
+    return aCache.getPropertySetInfo(rxPropSetInfo);
 }
 
 //  XPropertySet
diff --git a/sc/source/ui/view/spellcheckcontext.cxx 
b/sc/source/ui/view/spellcheckcontext.cxx
index 03e7d2304f11..b5bb141e54b5 100644
--- a/sc/source/ui/view/spellcheckcontext.cxx
+++ b/sc/source/ui/view/spellcheckcontext.cxx
@@ -12,6 +12,7 @@
 #include <editeng/eeitem.hxx>
 #include <editeng/langitem.hxx>
 #include <editeng/unolingu.hxx>
+#include <vcl/dropcache.hxx>
 
 #include <scitems.hxx>
 #include <document.hxx>
@@ -29,7 +30,7 @@ using namespace css;
 
 using sc::SpellCheckContext;
 
-class SpellCheckContext::SpellCheckCache
+class SpellCheckContext::SpellCheckCache : public CacheOwner
 {
     struct CellPos
     {
@@ -90,6 +91,20 @@ class SpellCheckContext::SpellCheckCache
     SharedStringMapType  maStringMisspells;
     CellMapType          maEditTextMisspells;
 
+    virtual void dropCaches() override
+    {
+        clear();
+    }
+
+    virtual void dumpState(rtl::OStringBuffer& rState) override
+    {
+        rState.append("
SpellCheckCache:        ");
+        rState.append("         string misspells: ");
+        rState.append(static_cast<sal_Int32>(maStringMisspells.size()));
+        rState.append("         editeng misspells: ");
+        rState.append(static_cast<sal_Int32>(maEditTextMisspells.size()));
+    }
+
 public:
 
     SpellCheckCache()
diff --git a/sw/source/core/ole/ndole.cxx b/sw/source/core/ole/ndole.cxx
index 6efa8bbd7c33..8c95d59e1cf6 100644
--- a/sw/source/core/ole/ndole.cxx
+++ b/sw/source/core/ole/ndole.cxx
@@ -33,6 +33,7 @@
 #include <sfx2/linkmgr.hxx>
 #include <unotools/configitem.hxx>
 #include <utility>
+#include <vcl/dropcache.hxx>
 #include <vcl/outdev.hxx>
 #include <fmtanchr.hxx>
 #include <frmfmt.hxx>
@@ -70,6 +71,7 @@ namespace {
 
 class SwOLELRUCache
     : private utl::ConfigItem
+    , public CacheOwner
 {
 private:
     std::deque<SwOLEObj *> m_OleObjects;
@@ -78,6 +80,19 @@ private:
 
     virtual void ImplCommit() override;
 
+    void tryShrinkCacheTo(sal_Int32 nVal);
+
+    virtual void dropCaches() override
+    {
+        tryShrinkCacheTo(0);
+    }
+
+    virtual void dumpState(rtl::OStringBuffer& rState) override
+    {
+        rState.append("
SwOLELRUCache:  ");
+        rState.append(static_cast<sal_Int32>(m_OleObjects.size()));
+    }
+
 public:
     SwOLELRUCache();
 
@@ -1305,6 +1320,23 @@ void SwOLELRUCache::ImplCommit()
 {
 }
 
+void SwOLELRUCache::tryShrinkCacheTo(sal_Int32 nVal)
+{
+    // size of cache has been changed
+    sal_Int32 nCount = m_OleObjects.size();
+    sal_Int32 nPos = nCount;
+
+    // try to remove the last entries until new maximum size is reached
+    while( nCount > nVal )
+    {
+        SwOLEObj *const pObj = m_OleObjects[ --nPos ];
+        if ( pObj->UnloadObject() )
+            nCount--;
+        if ( !nPos )
+            break;
+    }
+}
+
 void SwOLELRUCache::Load()
 {
     Sequence< OUString > aNames( GetPropertyNames() );
@@ -1316,25 +1348,11 @@ void SwOLELRUCache::Load()
 
     sal_Int32 nVal = 0;
     *pValues >>= nVal;
-
     if (nVal < m_nLRU_InitSize)
     {
         std::shared_ptr<SwOLELRUCache> xKeepAlive(g_pOLELRU_Cache); // prevent 
delete this
-        // size of cache has been changed
-        sal_Int32 nCount = m_OleObjects.size();
-        sal_Int32 nPos = nCount;
-
-        // try to remove the last entries until new maximum size is reached
-        while( nCount > nVal )
-        {
-            SwOLEObj *const pObj = m_OleObjects[ --nPos ];
-            if ( pObj->UnloadObject() )
-                nCount--;
-            if ( !nPos )
-                break;
-        }
+        tryShrinkCacheTo(nVal);
     }
-
     m_nLRU_InitSize = nVal;
 }
 
diff --git a/vcl/inc/graphic/Manager.hxx b/vcl/inc/graphic/Manager.hxx
index cac1a24beecf..efef7f5ed683 100644
--- a/vcl/inc/graphic/Manager.hxx
+++ b/vcl/inc/graphic/Manager.hxx
@@ -11,6 +11,7 @@
 
 #include <sal/types.h>
 #include <rtl/strbuf.hxx>
+#include <vcl/dropcache.hxx>
 #include <vcl/timer.hxx>
 
 #include <memory>
@@ -22,7 +23,7 @@ namespace vcl::graphic
 {
 class MemoryManaged;
 
-class VCL_DLLPUBLIC MemoryManager final
+class VCL_DLLPUBLIC MemoryManager final : public CacheOwner
 {
 private:
     o3tl::sorted_vector<MemoryManaged*> maObjectList;
@@ -55,8 +56,8 @@ public:
     void checkStartReduceTimer();
     void reduceMemory(std::unique_lock<std::mutex>& rGuard, bool bDropAll = 
false);
     void loopAndReduceMemory(std::unique_lock<std::mutex>& rGuard, bool 
bDropAll = false);
-    void reduceAllAndNow();
-    void dumpState(rtl::OStringBuffer& rState);
+    virtual void dropCaches() override;
+    virtual void dumpState(rtl::OStringBuffer& rState) override;
 };
 
 } // end namespace vcl::graphic
diff --git a/vcl/inc/svdata.hxx b/vcl/inc/svdata.hxx
index 86b07bce6caa..9311b0ffbb33 100644
--- a/vcl/inc/svdata.hxx
+++ b/vcl/inc/svdata.hxx
@@ -23,11 +23,13 @@
 
 #include <o3tl/lru_map.hxx>
 #include <o3tl/hash_combine.hxx>
+#include <o3tl/sorted_vector.hxx>
 #include <osl/conditn.hxx>
 #include <tools/fldunit.hxx>
 #include <unotools/options.hxx>
 #include <vcl/bitmapex.hxx>
 #include <vcl/cvtgrf.hxx>
+#include <vcl/dropcache.hxx>
 #include <vcl/image.hxx>
 #include <vcl/settings.hxx>
 #include <vcl/svapp.hxx>
@@ -402,6 +404,7 @@ struct ImplSVData
     css::uno::Reference< css::lang::XComponent > mxAccessBridge;
     std::unique_ptr<vcl::SettingsConfigItem> mpSettingsConfigItem;
     std::unordered_map< int, OUString > maPaperNames;
+    o3tl::sorted_vector<CacheOwner*> maCacheOwners;
 
     css::uno::Reference<css::i18n::XCharacterClassification> m_xCharClass;
 
@@ -419,6 +422,8 @@ struct ImplSVData
     LibreOfficeKitWakeCallback mpWakeCallback = nullptr;
     void *mpPollClosure = nullptr;
 
+    void registerCacheOwner(CacheOwner&);
+    void deregisterCacheOwner(CacheOwner&);
     void dropCaches();
     void dumpState(rtl::OStringBuffer &rState);
 };
diff --git a/vcl/source/app/svapp.cxx b/vcl/source/app/svapp.cxx
index 5740116238cb..f8e0e2c5319e 100644
--- a/vcl/source/app/svapp.cxx
+++ b/vcl/source/app/svapp.cxx
@@ -1778,8 +1778,6 @@ void dumpState(rtl::OStringBuffer &rState)
         pWin = Application::GetNextTopLevelWindow( pWin );
     }
 
-    vcl::graphic::MemoryManager::get().dumpState(rState);
-
     pSVData->dumpState(rState);
 
 #ifndef NDEBUG
@@ -1795,7 +1793,6 @@ void trimMemory(int nTarget)
         if (!pSVData) // shutting down
             return;
         pSVData->dropCaches();
-        vcl::graphic::MemoryManager::get().reduceAllAndNow();
     }
     // else for now caches re-fill themselves as/when used.
 }
diff --git a/vcl/source/app/svdata.cxx b/vcl/source/app/svdata.cxx
index 414de68e7f6c..1cdda1edb520 100644
--- a/vcl/source/app/svdata.cxx
+++ b/vcl/source/app/svdata.cxx
@@ -424,6 +424,16 @@ ImplSVData::ImplSVData()
     mpWinData = &private_aImplSVWinData::get();
 }
 
+void ImplSVData::registerCacheOwner(CacheOwner& rCacheOwner)
+{
+    maCacheOwners.insert(&rCacheOwner);
+}
+
+void ImplSVData::deregisterCacheOwner(CacheOwner& rCacheOwner)
+{
+    maCacheOwners.erase(&rCacheOwner);
+}
+
 void ImplSVData::dropCaches()
 {
     // we are iterating over a map and doing erase while inside a loop which 
is doing erase
@@ -433,6 +443,28 @@ void ImplSVData::dropCaches()
 
     maGDIData.maThemeDrawCommandsCache.clear();
     maGDIData.maThemeImageCache.clear();
+    mpBlendFrameCache.reset();
+
+    // copy, some caches self-delete on emptying, e.g. SwOLELRUCache
+    auto aCacheOwners = maCacheOwners;
+    for (CacheOwner* pCacheOwner : aCacheOwners)
+        pCacheOwner->dropCaches();
+}
+
+CacheOwner::CacheOwner()
+{
+    if (ImplSVData* pSVData = ImplGetSVData())
+    {
+        pSVData->registerCacheOwner(*this);
+        return;
+    }
+    SAL_WARN("vcl.app", "Cache owner ctor before ImplSVData created. This is 
useless.");
+}
+
+CacheOwner::~CacheOwner()
+{
+    if (ImplSVData* pSVData = ImplGetSVData())
+        pSVData->deregisterCacheOwner(*this);
 }
 
 void ImplSVData::dumpState(rtl::OStringBuffer &rState)
@@ -449,6 +481,18 @@ void ImplSVData::dumpState(rtl::OStringBuffer &rState)
         rState.append("x");
         rState.append(static_cast<sal_Int32>(it->first.maDestSize.Height()));
     }
+
+    if (mpBlendFrameCache)
+    {
+        rState.append("
BlendFrameCache:");
+        rState.append("
        ");
+        
rState.append(static_cast<sal_Int32>(mpBlendFrameCache->m_aLastSize.Width()));
+        rState.append("x");
+        
rState.append(static_cast<sal_Int32>(mpBlendFrameCache->m_aLastSize.Height()));
+    }
+
+    for (CacheOwner* pCacheOwner : maCacheOwners)
+        pCacheOwner->dumpState(rState);
 }
 
 ImplSVHelpData* CreateSVHelpData()
diff --git a/vcl/source/gdi/impglyphitem.cxx b/vcl/source/gdi/impglyphitem.cxx
index 6001ccd0236b..78137c73370e 100644
--- a/vcl/source/gdi/impglyphitem.cxx
+++ b/vcl/source/gdi/impglyphitem.cxx
@@ -590,4 +590,12 @@ size_t SalLayoutGlyphsCache::GlyphsCost::operator()(const 
SalLayoutGlyphs& glyph
     return cost;
 }
 
+void SalLayoutGlyphsCache::dropCaches() { clear(); }
+
+void SalLayoutGlyphsCache::dumpState(rtl::OStringBuffer& rState)
+{
+    rState.append("
SalLayoutGlyphsCache:   ");
+    rState.append(static_cast<sal_Int32>(mCachedGlyphs.size()));
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/Manager.cxx b/vcl/source/graphic/Manager.cxx
index 3c765f039584..17f25cedec7c 100644
--- a/vcl/source/graphic/Manager.cxx
+++ b/vcl/source/graphic/Manager.cxx
@@ -119,7 +119,7 @@ void MemoryManager::swappedOut(MemoryManaged* 
pMemoryManaged, sal_Int64 nNewSize
     changeExisting(pMemoryManaged, nNewSize);
 }
 
-void MemoryManager::reduceAllAndNow()
+void MemoryManager::dropCaches()
 {
     std::unique_lock aGuard(maMutex);
     reduceMemory(aGuard, true);
diff --git a/vcl/source/outdev/textline.cxx b/vcl/source/outdev/textline.cxx
index 3f2ba547184d..d31de8494c59 100644
--- a/vcl/source/outdev/textline.cxx
+++ b/vcl/source/outdev/textline.cxx
@@ -25,6 +25,7 @@
 #include <o3tl/lru_map.hxx>
 #include <comphelper/configuration.hxx>
 #include <tools/lazydelete.hxx>
+#include <vcl/dropcache.hxx>
 #include <vcl/metaact.hxx>
 #include <vcl/settings.hxx>
 #include <vcl/virdev.hxx>
@@ -40,7 +41,7 @@
 #define STRIKEOUT_LAST      STRIKEOUT_X
 
 namespace {
-    struct WavyLineCache final
+    struct WavyLineCache final : public CacheOwner
     {
         WavyLineCache () : m_aItems( 10 ) {}
 
@@ -66,6 +67,17 @@ namespace {
             rOutput = aBitmap;
         }
 
+        virtual void dropCaches() override
+        {
+            m_aItems.clear();
+        }
+
+        virtual void dumpState(rtl::OStringBuffer& rState) override
+        {
+            rState.append("
WavyLineCache:  ");
+            rState.append(static_cast<sal_Int32>(m_aItems.size()));
+        }
+
         private:
         struct WavyLineCacheItem
         {
diff --git a/vcl/source/text/TextLayoutCache.cxx 
b/vcl/source/text/TextLayoutCache.cxx
index 3e3571dfc861..1a7c6ab521cf 100644
--- a/vcl/source/text/TextLayoutCache.cxx
+++ b/vcl/source/text/TextLayoutCache.cxx
@@ -23,9 +23,11 @@
 
 #include <o3tl/hash_combine.hxx>
 #include <o3tl/lru_map.hxx>
+#include <rtl/strbuf.hxx>
 #include <unotools/configmgr.hxx>
 #include <tools/lazydelete.hxx>
 #include <officecfg/Office/Common.hxx>
+#include <vcl/dropcache.hxx>
 
 namespace vcl::text
 {
@@ -48,25 +50,56 @@ struct TextLayoutCacheCost
         return item->runs.size() * sizeof(item->runs.front());
     }
 };
-} // namespace
 
-std::shared_ptr<const TextLayoutCache> TextLayoutCache::Create(OUString const& 
rString)
+struct TextLayoutCacheMap : public CacheOwner
 {
     typedef o3tl::lru_map<OUString, std::shared_ptr<const TextLayoutCache>, 
FirstCharsStringHash,
                           FastStringCompareEqual, TextLayoutCacheCost>
         Cache;
-    static tools::DeleteOnDeinit<Cache> cache(
-        !comphelper::IsFuzzing() ? 
officecfg::Office::Common::Cache::Font::TextRunsCacheSize::get()
-                                 : 100);
-    if (Cache* map = cache.get())
+
+    Cache cache;
+
+    TextLayoutCacheMap(int capacity)
+        : cache(capacity)
     {
-        auto it = map->find(rString);
-        if (it != map->end())
+    }
+
+    std::shared_ptr<const TextLayoutCache> Create(OUString const& rString)
+    {
+        auto it = cache.find(rString);
+        if (it != cache.end())
             return it->second;
         auto ret = std::make_shared<const TextLayoutCache>(rString.getStr(), 
rString.getLength());
-        map->insert({ rString, ret });
+        cache.insert({ rString, ret });
         return ret;
     }
+
+    virtual void dropCaches() override { cache.clear(); }
+
+    virtual void dumpState(rtl::OStringBuffer& rState) override
+    {
+        rState.append("
TextLayoutCache:        ");
+        rState.append(static_cast<sal_Int32>(cache.size()));
+
+        TextLayoutCacheCost cost;
+        size_t nTotalCost = 0;
+        for (auto it = cache.begin(); it != cache.end(); ++it)
+            nTotalCost += cost(it->second);
+
+        rState.append("         cost: ");
+        rState.append(static_cast<sal_Int64>(nTotalCost));
+    }
+};
+
+} // namespace
+
+std::shared_ptr<const TextLayoutCache> TextLayoutCache::Create(OUString const& 
rString)
+{
+    static tools::DeleteOnDeinit<TextLayoutCacheMap> cache(
+        !comphelper::IsFuzzing() ? 
officecfg::Office::Common::Cache::Font::TextRunsCacheSize::get()
+                                 : 100);
+    if (TextLayoutCacheMap* map = cache.get())
+        return map->Create(rString);
     return std::make_shared<const TextLayoutCache>(rString.getStr(), 
rString.getLength());
 }
 }
diff --git a/vcl/unx/generic/fontmanager/fontconfig.cxx 
b/vcl/unx/generic/fontmanager/fontconfig.cxx
index 061e5256fc1c..80054e28bc7f 100644
--- a/vcl/unx/generic/fontmanager/fontconfig.cxx
+++ b/vcl/unx/generic/fontmanager/fontconfig.cxx
@@ -27,6 +27,7 @@
 #include <unx/fontmanager.hxx>
 #include <unx/helper.hxx>
 #include <comphelper/sequence.hxx>
+#include <vcl/dropcache.hxx>
 #include <vcl/svapp.hxx>
 #include <vcl/vclenum.hxx>
 #include <font/FontSelectPattern.hxx>
@@ -113,7 +114,7 @@ struct FcPatternDeleter
 
 typedef std::unique_ptr<FcPattern, FcPatternDeleter> FcPatternUniquePtr;
 
-class CachedFontConfigFontOptions
+class CachedFontConfigFontOptions : public CacheOwner
 {
 private:
     o3tl::lru_map<FontOptionsKey, FcPatternUniquePtr> lru_options_cache;
@@ -137,6 +138,17 @@ public:
         lru_options_cache.insert(std::make_pair(rKey, 
FcPatternUniquePtr(FcPatternDuplicate(pPattern))));
     }
 
+private:
+    virtual void dropCaches() override
+    {
+        lru_options_cache.clear();
+    }
+
+    virtual void dumpState(rtl::OStringBuffer& rState) override
+    {
+        rState.append("
CachedFontConfigFontOptions:    ");
+        rState.append(static_cast<sal_Int32>(lru_options_cache.size()));
+    }
 };
 
 typedef std::pair<FcChar8*, FcChar8*> lang_and_element;
diff --git a/vcl/unx/generic/gdi/cairotextrender.cxx 
b/vcl/unx/generic/gdi/cairotextrender.cxx
index 06578086a3ef..a00d0db9b544 100644
--- a/vcl/unx/generic/gdi/cairotextrender.cxx
+++ b/vcl/unx/generic/gdi/cairotextrender.cxx
@@ -43,7 +43,7 @@ namespace {
 
 typedef struct FT_FaceRec_* FT_Face;
 
-class CairoFontsCache
+class CairoFontsCache : public CacheOwner
 {
 public:
     struct CacheId
@@ -62,16 +62,30 @@ public:
     };
 
 private:
-    typedef         std::deque< std::pair<cairo_font_face_t*, CacheId> > 
LRUFonts;
-    static LRUFonts maLRUFonts;
-public:
-                                CairoFontsCache() = delete;
+    typedef std::deque< std::pair<cairo_font_face_t*, CacheId> > LRUFonts;
+    LRUFonts maLRUFonts;
+
+    virtual void dropCaches() override
+    {
+        maLRUFonts.clear();
+    }
+
+    virtual void dumpState(rtl::OStringBuffer& rState) override
+    {
+        rState.append("
CairoFontsCache:        ");
+        rState.append(static_cast<sal_Int32>(maLRUFonts.size()));
+    }
 
-    static void                 CacheFont(cairo_font_face_t* pFont, const 
CacheId &rId);
-    static cairo_font_face_t*   FindCachedFont(const CacheId &rId);
+public:
+    void               CacheFont(cairo_font_face_t* pFont, const CacheId &rId);
+    cairo_font_face_t* FindCachedFont(const CacheId &rId);
 };
 
-CairoFontsCache::LRUFonts CairoFontsCache::maLRUFonts;
+CairoFontsCache& getCairoFontsCache()
+{
+    static CairoFontsCache aCache;
+    return aCache;
+}
 
 void CairoFontsCache::CacheFont(cairo_font_face_t* pFont, const 
CairoFontsCache::CacheId &rId)
 {
@@ -188,13 +202,14 @@ CairoTextRender::~CairoTextRender()
 static void ApplyFont(cairo_t* cr, const CairoFontsCache::CacheId& rId, double 
nWidth, double nHeight, int nGlyphRotation,
                       const GenericSalLayout& rLayout)
 {
-    cairo_font_face_t* font_face = CairoFontsCache::FindCachedFont(rId);
+    CairoFontsCache& rCache = getCairoFontsCache();
+    cairo_font_face_t* font_face = rCache.FindCachedFont(rId);
     if (!font_face)
     {
         const FontConfigFontOptions *pOptions = rId.mpOptions;
         FcPattern *pPattern = pOptions->GetPattern();
         font_face = cairo_ft_font_face_create_for_pattern(pPattern);
-        CairoFontsCache::CacheFont(font_face, rId);
+        rCache.CacheFont(font_face, rId);
     }
     cairo_set_font_face(cr, font_face);
 

Reply via email to