summaryrefslogtreecommitdiffstats
path: root/vcl/source/font/fontcache.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--vcl/source/font/fontcache.cxx279
1 files changed, 279 insertions, 0 deletions
diff --git a/vcl/source/font/fontcache.cxx b/vcl/source/font/fontcache.cxx
new file mode 100644
index 000000000..9a87d02bc
--- /dev/null
+++ b/vcl/source/font/fontcache.cxx
@@ -0,0 +1,279 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <sal/log.hxx>
+
+#include <font/PhysicalFontCollection.hxx>
+#include <font/PhysicalFontFace.hxx>
+#include <font/PhysicalFontFamily.hxx>
+#include <fontinstance.hxx>
+#include <impfontcache.hxx>
+
+using namespace vcl::font;
+
+size_t ImplFontCache::IFSD_Hash::operator()( const vcl::font::FontSelectPattern& rFSD ) const
+{
+ return rFSD.hashCode();
+}
+
+bool ImplFontCache::IFSD_Equal::operator()(const vcl::font::FontSelectPattern& rA, const vcl::font::FontSelectPattern& rB) const
+{
+ // check normalized font family name
+ if( rA.maSearchName != rB.maSearchName )
+ return false;
+
+ // check font transformation
+ if( (rA.mnHeight != rB.mnHeight)
+ || (rA.mnWidth != rB.mnWidth)
+ || (rA.mnOrientation != rB.mnOrientation) )
+ return false;
+
+ // check mapping relevant attributes
+ if( (rA.mbVertical != rB.mbVertical)
+ || (rA.meLanguage != rB.meLanguage) )
+ return false;
+
+ // check font face attributes
+ if( (rA.GetWeight() != rB.GetWeight())
+ || (rA.GetItalic() != rB.GetItalic())
+// || (rA.meFamily != rB.meFamily) // TODO: remove this mostly obsolete member
+ || (rA.GetPitch() != rB.GetPitch()) )
+ return false;
+
+ // check style name
+ if( rA.GetStyleName() != rB.GetStyleName() )
+ return false;
+
+ // Symbol fonts may recode from one type to another So they are only
+ // safely equivalent for equal targets
+ if (rA.IsSymbolFont() || rB.IsSymbolFont())
+ {
+ if (rA.maTargetName != rB.maTargetName)
+ return false;
+ }
+
+ // check for features
+ if ((rA.maTargetName.indexOf(vcl::font::FontSelectPattern::FEAT_PREFIX)
+ != -1 ||
+ rB.maTargetName.indexOf(vcl::font::FontSelectPattern::FEAT_PREFIX)
+ != -1) && rA.maTargetName != rB.maTargetName)
+ return false;
+
+ if (rA.mbEmbolden != rB.mbEmbolden)
+ return false;
+
+ if (rA.maItalicMatrix != rB.maItalicMatrix)
+ return false;
+
+ return true;
+}
+
+ImplFontCache::ImplFontCache()
+ : mpLastHitCacheEntry( nullptr )
+ , maFontInstanceList(std::numeric_limits<size_t>::max()) // "unlimited", i.e. no cleanup
+ // The cache limit is set by the rough number of characters needed to read your average Asian newspaper.
+ , m_aBoundRectCache(3000)
+{}
+
+ImplFontCache::~ImplFontCache()
+{
+ for (const auto & rLFI : maFontInstanceList)
+ {
+ rLFI.second->mpFontCache = nullptr;
+ }
+}
+
+rtl::Reference<LogicalFontInstance> ImplFontCache::GetFontInstance( PhysicalFontCollection const * pFontList,
+ const vcl::Font& rFont, const Size& rSize, float fExactHeight, bool bNonAntialias )
+{
+ // initialize internal font request object
+ vcl::font::FontSelectPattern aFontSelData(rFont, rFont.GetFamilyName(), rSize, fExactHeight, bNonAntialias);
+ return GetFontInstance( pFontList, aFontSelData );
+}
+
+rtl::Reference<LogicalFontInstance> ImplFontCache::GetFontInstance( PhysicalFontCollection const * pFontList,
+ vcl::font::FontSelectPattern& aFontSelData )
+{
+ rtl::Reference<LogicalFontInstance> pFontInstance;
+ PhysicalFontFamily* pFontFamily = nullptr;
+
+ // check if a directly matching logical font instance is already cached,
+ // the most recently used font usually has a hit rate of >50%
+ if (mpLastHitCacheEntry && IFSD_Equal()(aFontSelData, mpLastHitCacheEntry->GetFontSelectPattern()))
+ pFontInstance = mpLastHitCacheEntry;
+ else
+ {
+ FontInstanceList::const_iterator it = maFontInstanceList.find( aFontSelData );
+ if( it != maFontInstanceList.end() )
+ pFontInstance = (*it).second;
+ }
+
+ if( !pFontInstance ) // no direct cache hit
+ {
+ // find the best matching logical font family and update font selector accordingly
+ pFontFamily = pFontList->FindFontFamily( aFontSelData );
+ SAL_WARN_IF( (pFontFamily == nullptr), "vcl", "ImplFontCache::Get() No logical font found!" );
+ if( pFontFamily )
+ {
+ aFontSelData.maSearchName = pFontFamily->GetSearchName();
+
+ // check if an indirectly matching logical font instance is already cached
+ FontInstanceList::const_iterator it = maFontInstanceList.find( aFontSelData );
+ if( it != maFontInstanceList.end() )
+ pFontInstance = (*it).second;
+ }
+ }
+
+ if( !pFontInstance && pFontFamily) // still no cache hit => create a new font instance
+ {
+ vcl::font::PhysicalFontFace* pFontData = pFontFamily->FindBestFontFace(aFontSelData);
+
+ // create a new logical font instance from this physical font face
+ pFontInstance = pFontData->CreateFontInstance( aFontSelData );
+ pFontInstance->mpFontCache = this;
+
+ // if we're substituting from or to a symbol font we may need a symbol
+ // conversion table
+ if( pFontData->IsSymbolFont() || aFontSelData.IsSymbolFont() )
+ {
+ if( aFontSelData.maTargetName != aFontSelData.maSearchName )
+ pFontInstance->mpConversion = ConvertChar::GetRecodeData( aFontSelData.maTargetName, aFontSelData.maSearchName );
+ }
+
+#ifdef MACOSX
+ //It might be better to dig out the font version of the target font
+ //to see if it's a modern re-coded apple symbol font in case that
+ //font shows up on a different platform
+ if (!pFontInstance->mpConversion &&
+ aFontSelData.maTargetName.equalsIgnoreAsciiCase("symbol") &&
+ aFontSelData.maSearchName.equalsIgnoreAsciiCase("symbol"))
+ {
+ pFontInstance->mpConversion = ConvertChar::GetRecodeData( u"Symbol", u"AppleSymbol" );
+ }
+#endif
+
+ static const size_t FONTCACHE_MAX = getenv("LO_TESTNAME") ? 1 : 50;
+
+ if (maFontInstanceList.size() >= FONTCACHE_MAX)
+ {
+ struct limit_exception : public std::exception {};
+ try
+ {
+ maFontInstanceList.remove_if([this] (FontInstanceList::key_value_pair_t const& rFontPair)
+ {
+ if (maFontInstanceList.size() < FONTCACHE_MAX)
+ throw limit_exception();
+ LogicalFontInstance* pFontEntry = rFontPair.second.get();
+ if (pFontEntry->m_nCount > 1)
+ return false;
+ m_aBoundRectCache.remove_if([&pFontEntry] (GlyphBoundRectCache::key_value_pair_t const& rGlyphPair)
+ { return rGlyphPair.first.m_pFont == pFontEntry; });
+ if (mpLastHitCacheEntry == pFontEntry)
+ mpLastHitCacheEntry = nullptr;
+ return true;
+ });
+ }
+ catch (limit_exception&) {}
+ }
+
+ assert(pFontInstance);
+ // add the new entry to the cache
+ maFontInstanceList.insert({aFontSelData, pFontInstance.get()});
+ }
+
+ mpLastHitCacheEntry = pFontInstance.get();
+ return pFontInstance;
+}
+
+rtl::Reference<LogicalFontInstance> ImplFontCache::GetGlyphFallbackFont( PhysicalFontCollection const * pFontCollection,
+ vcl::font::FontSelectPattern& rFontSelData, LogicalFontInstance* pFontInstance, int nFallbackLevel, OUString& rMissingCodes )
+{
+ // get a candidate font for glyph fallback
+ // unless the previously selected font got a device specific substitution
+ // e.g. PsPrint Arial->Helvetica for udiaeresis when Helvetica doesn't support it
+ if( nFallbackLevel >= 1)
+ {
+ PhysicalFontFamily* pFallbackData = nullptr;
+
+ //fdo#33898 If someone has EUDC installed then they really want that to
+ //be used as the first-choice glyph fallback seeing as it's filled with
+ //private area codes with don't make any sense in any other font so
+ //prioritize it here if it's available. Ideally we would remove from
+ //rMissingCodes all the glyphs which it is able to resolve as an
+ //optimization, but that's tricky to achieve cross-platform without
+ //sufficient heavy-weight code that's likely to undo the value of the
+ //optimization
+ if (nFallbackLevel == 1)
+ pFallbackData = pFontCollection->FindFontFamily(u"EUDC");
+ if (!pFallbackData)
+ pFallbackData = pFontCollection->GetGlyphFallbackFont(rFontSelData, pFontInstance, rMissingCodes, nFallbackLevel-1);
+ // escape when there are no font candidates
+ if( !pFallbackData )
+ return nullptr;
+ // override the font name
+ rFontSelData.SetFamilyName( pFallbackData->GetFamilyName() );
+ // clear the cached normalized name
+ rFontSelData.maSearchName.clear();
+ }
+
+ rtl::Reference<LogicalFontInstance> pFallbackFont = GetFontInstance( pFontCollection, rFontSelData );
+ return pFallbackFont;
+}
+
+void ImplFontCache::Invalidate()
+{
+ // #112304# make sure the font cache is really clean
+ mpLastHitCacheEntry = nullptr;
+ for (auto const & pair : maFontInstanceList)
+ pair.second->mpFontCache = nullptr;
+ maFontInstanceList.clear();
+ m_aBoundRectCache.clear();
+}
+
+bool ImplFontCache::GetCachedGlyphBoundRect(const LogicalFontInstance *pFont, sal_GlyphId nID, tools::Rectangle &rRect)
+{
+ if (!pFont->GetFontCache())
+ return false;
+ assert(pFont->GetFontCache() == this);
+ if (pFont->GetFontCache() != this)
+ return false;
+
+ auto it = m_aBoundRectCache.find({pFont, nID});
+ if (it != m_aBoundRectCache.end())
+ {
+ rRect = it->second;
+ return true;
+ }
+ return false;
+}
+
+void ImplFontCache::CacheGlyphBoundRect(const LogicalFontInstance *pFont, sal_GlyphId nID, tools::Rectangle &rRect)
+{
+ if (!pFont->GetFontCache())
+ return;
+ assert(pFont->GetFontCache() == this);
+ if (pFont->GetFontCache() != this)
+ return;
+
+ m_aBoundRectCache.insert({{pFont, nID}, rRect});
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */